home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 1843 / 1843.xpi / content / firebug / debugger.js < prev    next >
Text File  |  2010-01-15  |  100KB  |  3,154 lines

  1. /* See license.txt for terms of usage */
  2.  
  3. FBL.ns(function() { with (FBL) {
  4.  
  5. // ************************************************************************************************
  6. // Constants
  7.  
  8. const Cc = Components.classes;
  9. const Ci = Components.interfaces;
  10. const jsdIScript = Ci.jsdIScript;
  11. const jsdIStackFrame = Ci.jsdIStackFrame;
  12. const jsdIExecutionHook = Ci.jsdIExecutionHook;
  13. const nsISupports = Ci.nsISupports;
  14. const nsICryptoHash = Ci.nsICryptoHash;
  15. const nsIURI = Ci.nsIURI;
  16.  
  17. const PCMAP_SOURCETEXT = jsdIScript.PCMAP_SOURCETEXT;
  18. const PCMAP_PRETTYPRINT = jsdIScript.PCMAP_PRETTYPRINT;
  19.  
  20. const RETURN_VALUE = jsdIExecutionHook.RETURN_RET_WITH_VAL;
  21. const RETURN_THROW_WITH_VAL = jsdIExecutionHook.RETURN_THROW_WITH_VAL;
  22. const RETURN_CONTINUE = jsdIExecutionHook.RETURN_CONTINUE;
  23. const RETURN_CONTINUE_THROW = jsdIExecutionHook.RETURN_CONTINUE_THROW;
  24. const RETURN_ABORT = jsdIExecutionHook.RETURN_ABORT;
  25.  
  26. const TYPE_THROW = jsdIExecutionHook.TYPE_THROW;
  27. const TYPE_DEBUGGER_KEYWORD = jsdIExecutionHook.TYPE_DEBUGGER_KEYWORD;
  28.  
  29. const STEP_OVER = 1;
  30. const STEP_INTO = 2;
  31. const STEP_OUT = 3;
  32.  
  33. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  34.  
  35. const tooltipTimeout = 300;
  36.  
  37. const reLineNumber = /^[^\\]?#(\d*)$/;
  38.  
  39. const reEval =  /\s*eval\s*\(([^)]*)\)/m;        // eval ( $1 )
  40. const reHTM = /\.[hH][tT][mM]/;
  41. const reFunction = /\s*Function\s*\(([^)]*)\)/m;
  42. const reTooMuchRecursion = /too\smuch\srecursion/;
  43.  
  44. // ************************************************************************************************
  45.  
  46. Firebug.Debugger = extend(Firebug.ActivableModule,
  47. {
  48.     dispatchName: "debugger",
  49.     fbs: fbs, // access to firebug-service in chromebug under browser.xul.DOM.Firebug.Debugger.fbs /*@explore*/
  50.  
  51.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  52.     // Debugging
  53.  
  54.     evaluate: function(js, context, scope)
  55.     {
  56.         var frame = context.currentFrame;
  57.         if (!frame)
  58.             return;
  59.  
  60.         frame.scope.refresh(); // XXX what's this do?
  61.  
  62.         var result = {};
  63.         var scriptToEval = js;
  64.  
  65.         // This seem to be safe; eval'ing a getter property in content that tries to
  66.         // be evil and get Components.classes results in a permission denied error.
  67.         var ok = frame.eval(scriptToEval, "", 1, result);
  68.  
  69.         var value = unwrapIValue(result.value);
  70.         if (ok)
  71.             return value;
  72.         else
  73.             throw value;
  74.     },
  75.  
  76.     getCurrentFrameKeys: function(context)
  77.     {
  78.         var globals = keys(context.getGlobalScope().wrappedJSObject);  // return is safe
  79.  
  80.         if (context.currentFrame)
  81.             return this.getFrameKeys(context.currentFrame, globals);
  82.  
  83.         return globals;
  84.     },
  85.  
  86.     getFrameKeys: function(frame, names)
  87.     {
  88.         var listValue = {value: null}, lengthValue = {value: 0};
  89.         frame.scope.getProperties(listValue, lengthValue);
  90.  
  91.         for (var i = 0; i < lengthValue.value; ++i)
  92.         {
  93.             var prop = listValue.value[i];
  94.             var name = unwrapIValue(prop.name);
  95.             names.push(name);
  96.         }
  97.         return names;
  98.     },
  99.  
  100.     focusWatch: function(context)
  101.     {
  102.         if (Firebug.isDetached())
  103.             Firebug.chrome.focus();
  104.         else
  105.             Firebug.toggleBar(true);
  106.  
  107.         Firebug.chrome.selectPanel("script");
  108.  
  109.         var watchPanel = context.getPanel("watches", true);
  110.         if (watchPanel)
  111.         {
  112.             Firebug.CommandLine.isReadyElsePreparing(context);
  113.             watchPanel.editNewWatch();
  114.         }
  115.     },
  116.  
  117.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  118.  
  119.     beginInternalOperation: function() // stop debugger operations like breakOnErrors
  120.     {
  121.         var state = {breakOnErrors: Firebug.breakOnErrors};
  122.         Firebug.breakOnErrors = false;
  123.         return state;
  124.     },
  125.  
  126.     endInternalOperation: function(state)  // pass back the object given by beginInternalOperation
  127.     {
  128.         Firebug.breakOnErrors = state.breakOnErrors;
  129.         return true;
  130.     },
  131.  
  132.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  133.  
  134.     halt: function(fn)
  135.     {
  136.         this.haltCallback = fn; // called in this.onHalt as fn(frame);
  137.         fbs.halt(this);
  138.  
  139.         debuggerHalter(); // a function with a URL that passes jsdIFilter and says "debugger;"
  140.  
  141.         if (this.haltCallback) // so we have a second try
  142.         {
  143.             FBTrace.sysout("debugger did not halt jsd: ", jsd);
  144.             if (Firebug.CommandLine.isReadyElsePreparing(FirebugContext))
  145.                 Firebug.CommandLine.evaluate("debugger;", FirebugContext);
  146.         }
  147.  
  148.     },
  149.  
  150.     breakNow: function()
  151.     {
  152.         Firebug.Debugger.halt(function(frame)
  153.         {
  154.             for (; frame && frame.isValid; frame = frame.callingFrame)
  155.             {
  156.                 var fileName = frame.script.fileName;
  157.                 if (fileName && fileName.indexOf("chrome://firebug/") != 0 &&
  158.                     fileName.indexOf("/components/firebug-") == -1)
  159.                     break;
  160.             }
  161.  
  162.             if (frame)
  163.                 Firebug.Debugger.onBreak(frame, 3);
  164.             else
  165.             {
  166.                 // XXXrobc no-op, added for detrace
  167.             }
  168.         });
  169.     },
  170.  
  171.     stop: function(context, frame, type, rv)
  172.     {
  173.         if (context.stopped)
  174.             return RETURN_CONTINUE;
  175.  
  176.         if (!this.isAlwaysEnabled())
  177.             return RETURN_CONTINUE;
  178.  
  179.         var executionContext;
  180.         try
  181.         {
  182.             executionContext = frame.executionContext;
  183.         }
  184.         catch (exc)
  185.         {
  186.             return RETURN_CONTINUE;
  187.         }
  188.  
  189.         context.debugFrame = frame;
  190.         context.stopped = true;
  191.  
  192.         var hookReturn = dispatch2(this.fbListeners,"onStop",[context,frame, type,rv]);
  193.         if ( hookReturn && hookReturn >= 0 )
  194.         {
  195.             delete context.stopped;
  196.             delete context.debugFrame;
  197.             delete context;
  198.             return hookReturn;
  199.         }
  200.  
  201.         try
  202.         {
  203.             // We will pause here until resume is called
  204.             var depth = fbs.enterNestedEventLoop({onNest: bindFixed(this.startDebugging, this, context)});
  205.             // For some reason we don't always end up here
  206.         }
  207.         catch (exc)
  208.         {
  209.             // Just ignore exceptions that happened while in the nested loop
  210.         }
  211.  
  212.         this.stopDebugging(context);
  213.  
  214.         dispatch(this.fbListeners,"onResume",[context]);
  215.  
  216.         if (context.aborted)
  217.         {
  218.             delete context.aborted;
  219.             return RETURN_ABORT;
  220.         }
  221.         else
  222.             return RETURN_CONTINUE;
  223.     },
  224.  
  225.     resume: function(context)
  226.     {
  227.         this.thaw(context);
  228.  
  229.         var depth = fbs.exitNestedEventLoop();
  230.     },
  231.  
  232.     abort: function(context)
  233.     {
  234.         if (context.stopped)
  235.         {
  236.             context.aborted = true;
  237.             this.resume(context);
  238.         }
  239.     },
  240.  
  241.     stepOver: function(context)
  242.     {
  243.         if (!context.debugFrame || !context.debugFrame.isValid)
  244.             return;
  245.  
  246.         fbs.step(STEP_OVER, context.debugFrame, this);
  247.         this.resume(context);
  248.     },
  249.  
  250.     stepInto: function(context)
  251.     {
  252.         if (!context.debugFrame || !context.debugFrame.isValid)
  253.             return;
  254.  
  255.         fbs.step(STEP_INTO, context.debugFrame, this);
  256.         this.resume(context);
  257.     },
  258.  
  259.     stepOut: function(context)
  260.     {
  261.         if (!context.debugFrame || !context.debugFrame.isValid)
  262.             return;
  263.  
  264.         fbs.step(STEP_OUT, context.debugFrame);
  265.         this.resume(context);
  266.     },
  267.  
  268.     suspend: function(context)
  269.     {
  270.         if (context.stopped)
  271.             return;
  272.         fbs.suspend(this, context);
  273.     },
  274.  
  275.     unSuspend: function(context)
  276.     {
  277.         fbs.stopStepping();  // TODO per context
  278.     },
  279.  
  280.     runUntil: function(context, sourceFile, lineNo)
  281.     {
  282.         if (!context.debugFrame || !context.debugFrame.isValid)
  283.             return;
  284.  
  285.         fbs.runUntil(sourceFile, lineNo, context.debugFrame, this);
  286.         this.resume(context);
  287.     },
  288.  
  289.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  290.  
  291.     freeze: function(context)
  292.     {
  293.         var executionContext = context.debugFrame.executionContext;
  294.         try {
  295.             executionContext.scriptsEnabled = false;
  296.  
  297.             if (context.window instanceof Ci.nsIInterfaceRequestor)
  298.             {
  299.                 context.eventSuppressor = context.window.getInterface(Ci.nsIDOMWindowUtils);
  300.                 if (context.eventSuppressor)
  301.                     context.eventSuppressor.suppressEventHandling(true);
  302.             }
  303.  
  304.         } catch (exc) {
  305.             // This attribute is only valid for contexts which implement nsIScriptContext.
  306.         }
  307.     },
  308.  
  309.     thaw: function(context)
  310.     {
  311.         var executionContext = context.debugFrame.executionContext;
  312.         try {
  313.             if (executionContext.isValid)
  314.             {
  315.                 if (context.eventSuppressor)
  316.                 {
  317.                     context.eventSuppressor.suppressEventHandling(false);
  318.                     delete context.eventSuppressor;
  319.                 }
  320.  
  321.                 executionContext.scriptsEnabled = true;
  322.             }
  323.             else
  324.             {
  325.             }
  326.         } catch (exc) {
  327.         }
  328.  
  329.     },
  330.  
  331.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  332.     // Breakpoints
  333.  
  334.     setBreakpoint: function(sourceFile, lineNo)
  335.     {
  336.         fbs.setBreakpoint(sourceFile, lineNo, null, Firebug.Debugger);
  337.     },
  338.  
  339.     clearBreakpoint: function(sourceFile, lineNo)
  340.     {
  341.         fbs.clearBreakpoint(sourceFile.href, lineNo);
  342.     },
  343.  
  344.     setErrorBreakpoint: function(sourceFile, line)
  345.     {
  346.         fbs.setErrorBreakpoint(sourceFile, line, Firebug.Debugger);
  347.     },
  348.  
  349.     clearErrorBreakpoint: function(sourceFile, line)
  350.     {
  351.         fbs.clearErrorBreakpoint(sourceFile, line, Firebug.Debugger);
  352.     },
  353.  
  354.     clearAllBreakpoints: function(context)
  355.     {
  356.         if (context)
  357.         {
  358.             var sourceFiles = sourceFilesAsArray(context.sourceFileMap);
  359.             fbs.clearAllBreakpoints(sourceFiles, Firebug.Debugger);
  360.         }
  361.         else
  362.         {
  363.             fbs.enumerateBreakpoints(null, {call: function(url, lineNo, bp) // null means all urls
  364.             {
  365.                 if (bp.debugger !== this) // skip breakpoints of other debuggers.
  366.                     return;
  367.  
  368.                 if (Firebug.filterSystemURLs) // then there are not system urls, clear all
  369.                     fbs.clearBreakpoint(url, lineNo);
  370.                 else
  371.                 {
  372.                     if (!isSystemURL(url))  // if there are system urls, leave them
  373.                         fbs.clearBreakpoint(url, lineNo);
  374.                 }
  375.             }});
  376.         }
  377.     },
  378.  
  379.     enableAllBreakpoints: function(context)
  380.     {
  381.         for (var url in context.sourceFileMap)
  382.         {
  383.             fbs.enumerateBreakpoints(url, {call: function(url, lineNo)
  384.             {
  385.                 fbs.enableBreakpoint(url, lineNo);
  386.             }});
  387.         }
  388.     },
  389.  
  390.     disableAllBreakpoints: function(context)
  391.     {
  392.         for (var url in context.sourceFileMap)
  393.         {
  394.             fbs.enumerateBreakpoints(url, {call: function(url, lineNo)
  395.             {
  396.                 fbs.disableBreakpoint(url, lineNo);
  397.             }});
  398.         }
  399.     },
  400.  
  401.     getBreakpointCount: function(context)
  402.     {
  403.         var count = 0;
  404.         for (var url in context.sourceFileMap)
  405.         {
  406.             fbs.enumerateBreakpoints(url,
  407.             {
  408.                 call: function(url, lineNo)
  409.                 {
  410.                     ++count;
  411.                 }
  412.             });
  413.  
  414.             fbs.enumerateErrorBreakpoints(url,
  415.             {
  416.                 call: function(url, lineNo)
  417.                 {
  418.                     ++count;
  419.                 }
  420.             });
  421.         }
  422.         return count;
  423.     },
  424.  
  425.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  426.     // Debugging and monitoring
  427.  
  428.     traceAll: function(context)
  429.     {
  430.         fbs.traceAll(sourceURLsAsArray(context), this);
  431.     },
  432.  
  433.     untraceAll: function(context)
  434.     {
  435.         fbs.untraceAll(this);
  436.     },
  437.  
  438.     monitorFunction: function(fn, mode)
  439.     {
  440.         if (typeof(fn) == "function" || fn instanceof Function)
  441.         {
  442.             var script = findScriptForFunctionInContext(FirebugContext, fn);
  443.             if (script)
  444.                 this.monitorScript(fn, script, mode);
  445.         }
  446.     },
  447.  
  448.     unmonitorFunction: function(fn, mode)
  449.     {
  450.         if (typeof(fn) == "function" || fn instanceof Function)
  451.         {
  452.             var script = findScriptForFunctionInContext(FirebugContext, fn);
  453.             if (script)
  454.                 this.unmonitorScript(fn, script, mode);
  455.         }
  456.     },
  457.  
  458.     monitorScript: function(fn, script, mode)
  459.     {
  460.         var scriptInfo = Firebug.SourceFile.getSourceFileAndLineByScript(FirebugContext, script);
  461.         if (scriptInfo)
  462.         {
  463.             if (mode == "debug")
  464.                 this.setBreakpoint(scriptInfo.sourceFile, scriptInfo.lineNo, null, this);
  465.             else if (mode == "monitor")
  466.                 fbs.monitor(scriptInfo.sourceFile, scriptInfo.lineNo, Firebug.Debugger);
  467.         }
  468.     },
  469.  
  470.     unmonitorScript: function(fn, script, mode)
  471.     {
  472.         var scriptInfo = Firebug.SourceFile.getSourceFileAndLineByScript(FirebugContext, script);
  473.         if (scriptInfo)
  474.         {
  475.             if (mode == "debug")
  476.                 this.clearBreakpoint(scriptInfo.sourceFile, scriptInfo.lineNo);
  477.             else if (mode == "monitor")
  478.                 fbs.unmonitor(scriptInfo.sourceFile, scriptInfo.lineNo);
  479.         }
  480.     },
  481.  
  482.     traceCalls: function(context, fn)
  483.     {
  484.         if (typeof(fn) == "function" || fn instanceof Function)
  485.         {
  486.             var script = findScriptForFunctionInContext(context, fn);
  487.             if (script)
  488.                 this.traceScriptCalls(context, script);
  489.             else
  490.             {
  491.             }
  492.         }
  493.     },
  494.  
  495.     untraceCalls: function(context, fn)
  496.     {
  497.         if (typeof(fn) == "function" || fn instanceof Function)
  498.         {
  499.             var script = findScriptForFunctionInContext(context, fn);
  500.             if (script)
  501.                 this.untraceScriptCalls(context, script);
  502.         }
  503.     },
  504.  
  505.     traceScriptCalls: function(context, script)
  506.     {
  507.         var scriptInfo = Firebug.SourceFile.getSourceFileAndLineByScript(context, script);
  508.         if (scriptInfo)
  509.             fbs.traceCalls(scriptInfo.sourceFile, scriptInfo.lineNo, Firebug.Debugger);
  510.     },
  511.  
  512.     untraceScriptCalls: function(context, script)
  513.     {
  514.         var scriptInfo = Firebug.SourceFile.getSourceFileAndLineByScript(context, script);
  515.         if (scriptInfo)
  516.             fbs.untraceCalls(scriptInfo.sourceFile, scriptInfo.lineNo, Firebug.Debugger);
  517.     },
  518.  
  519.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  520.     // UI Stuff
  521.  
  522.     /*
  523.      * Called when a nestedEventLoop begins
  524.      */
  525.     startDebugging: function(context)
  526.     {
  527.         try {
  528.  
  529.             this.freeze(context);
  530.  
  531.             fbs.lockDebugger();
  532.  
  533.             context.currentFrame = context.debugFrame;
  534.  
  535.             context.executingSourceFile = Firebug.SourceFile.getSourceFileByScript(context, context.currentFrame.script);
  536.  
  537.             if (!context.executingSourceFile)  // bail out, we don't want the user stuck in debug with out source.
  538.             {
  539.                 this.resume(context);
  540.                 return;
  541.             }
  542.  
  543.             var currentBreakable = Firebug.chrome.getGlobalAttribute("cmd_breakOnNext", "breakable");
  544.  
  545.             if (currentBreakable == "false") // then we are armed but we broke
  546.                 Firebug.chrome.setGlobalAttribute("cmd_breakOnNext", "breakable", "true");
  547.  
  548.             if (context != FirebugContext || Firebug.isDetached())
  549.                 Firebug.showContext(context.browser, context);  // Make FirebugContext = context and sync the UI
  550.  
  551.             if (Firebug.isMinimized()) // then open the UI to show we are stopped
  552.                 Firebug.unMinimize();
  553.  
  554.             this.syncCommands(context);
  555.             this.syncListeners(context);
  556.  
  557.             // Update Break on Next lightning.
  558.             var panel = context.getPanel("script", true);
  559.             Firebug.Breakpoint.updatePanelTab(panel, false);
  560.  
  561.             Firebug.chrome.select(context.currentFrame, "script", null, true);
  562.             Firebug.chrome.focus();
  563.         }
  564.         catch(exc)
  565.         {
  566.             Firebug.Console.log("Resuming debugger: error during debugging loop: "+exc);
  567.             this.resume(context);
  568.         }
  569.  
  570.         dispatch(this.fbListeners, "onStartDebugging", [context]);
  571.  
  572.     },
  573.  
  574.     /*
  575.      * Called in the main event loop, from jsd, after we have exited the nested event loop
  576.      */
  577.  
  578.     stopDebugging: function(context)
  579.     {
  580.         try
  581.         {
  582.             fbs.unlockDebugger();
  583.  
  584.             // If the user reloads the page while the debugger is stopped, then
  585.             // the current context will be destroyed just before
  586.             if (context && context.window && !context.aborted)
  587.             {
  588.                 delete context.stopped;
  589.                 delete context.debugFrame;
  590.                 delete context.currentFrame;
  591.  
  592.                 var chrome = Firebug.chrome;
  593.  
  594.                 this.syncCommands(context);
  595.                 this.syncListeners(context);
  596.  
  597.                 chrome.syncSidePanels();
  598.  
  599.                 var panel = context.getPanel("script", true);
  600.                 if (panel && panel == Firebug.chrome.getSelectedPanel())
  601.                     panel.showNoStackFrame(); // unhighlight and remove toolbar-status line
  602.  
  603.                 if (panel)
  604.                     panel.highlight(false);
  605.  
  606.                 context.executingSourceFile = null;
  607.                 delete context.breakLineNumber;
  608.             }
  609.         }
  610.         catch (exc)
  611.         {
  612.             ERROR(exc);
  613.         }
  614.     },
  615.  
  616.     syncCommands: function(context)
  617.     {
  618.         var chrome = Firebug.chrome;
  619.         if (!chrome)
  620.         {
  621.             return;
  622.         }
  623.  
  624.         if (context.stopped)
  625.         {
  626.             chrome.setGlobalAttribute("fbDebuggerButtons", "stopped", "true");
  627.             chrome.setGlobalAttribute("cmd_resumeExecution", "disabled", "false");
  628.             chrome.setGlobalAttribute("cmd_stepOver", "disabled", "false");
  629.             chrome.setGlobalAttribute("cmd_stepInto", "disabled", "false");
  630.             chrome.setGlobalAttribute("cmd_stepOut", "disabled", "false");
  631.         }
  632.         else
  633.         {
  634.             chrome.setGlobalAttribute("fbDebuggerButtons", "stopped", "false");
  635.             chrome.setGlobalAttribute("cmd_stepOver", "disabled", "true");
  636.             chrome.setGlobalAttribute("cmd_stepInto", "disabled", "true");
  637.             chrome.setGlobalAttribute("cmd_stepOut", "disabled", "true");
  638.             chrome.setGlobalAttribute("cmd_resumeExecution", "disabled", "true");
  639.         }
  640.     },
  641.  
  642.     syncListeners: function(context)
  643.     {
  644.         var chrome = Firebug.chrome;
  645.  
  646.         if (context.stopped)
  647.             this.attachListeners(context, chrome);
  648.         else
  649.             this.detachListeners(context, chrome);
  650.     },
  651.  
  652.     attachListeners: function(context, chrome)
  653.     {
  654.         this.keyListeners =
  655.         [
  656.             chrome.keyCodeListen("F8", null, bind(this.resume, this, context), true),
  657.             chrome.keyListen("/", isControl, bind(this.resume, this, context)),
  658.             chrome.keyCodeListen("F10", null, bind(this.stepOver, this, context), true),
  659.             chrome.keyListen("'", isControl, bind(this.stepOver, this, context)),
  660.             chrome.keyCodeListen("F11", null, bind(this.stepInto, this, context)),
  661.             chrome.keyListen(";", isControl, bind(this.stepInto, this, context)),
  662.             chrome.keyCodeListen("F11", isShift, bind(this.stepOut, this, context)),
  663.             chrome.keyListen(",", isControlShift, bind(this.stepOut, this, context))
  664.         ];
  665.     },
  666.  
  667.     detachListeners: function(context, chrome)
  668.     {
  669.         if (this.keyListeners)
  670.         {
  671.             for (var i = 0; i < this.keyListeners.length; ++i)
  672.                 chrome.keyIgnore(this.keyListeners[i]);
  673.             delete this.keyListeners;
  674.         }
  675.     },
  676.  
  677.     showPanel: function(browser, panel)
  678.     {
  679.         if (panel && panel.name == "script")
  680.         {
  681.             this.syncCommands(panel.context);
  682.             this.ableWatchSidePanel(panel.context);
  683.         }
  684.     },
  685.  
  686.     suspendFirebug: function()
  687.     {
  688.         Firebug.suspendFirebug();
  689.     },
  690.  
  691.     resumeFirebug: function()
  692.     {
  693.         Firebug.resumeFirebug();
  694.     },
  695.  
  696.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  697.  
  698.     supportsWindow: function(win)
  699.     {
  700.         if (!this.isAlwaysEnabled())
  701.             return false;
  702.  
  703.         var context = ( (win && TabWatcher) ? TabWatcher.getContextByWindow(win) : null);
  704.  
  705.         this.breakContext = context;
  706.         return !!context;
  707.     },
  708.  
  709.     supportsGlobal: function(frameWin) // This is call from fbs for almost all fbs operations
  710.     {
  711.         var context = ( (frameWin && TabWatcher) ? TabWatcher.getContextByWindow(frameWin) : null);
  712.         if (!context)
  713.             return false;
  714.  
  715.         // Apparently the frameWin is a XPCSafeJSObjectWrapper that looks like a Window.
  716.         // Since this is method called a lot make a hacky fast check on _getFirebugConsoleElement
  717.         if (!frameWin._getFirebugConsoleElement && !context.stopped)
  718.         {
  719.             this.injectConsole(context, frameWin);
  720.         }
  721.         else
  722.         {
  723.         }
  724.  
  725.         this.breakContext = context;
  726.         //FBTrace.sysout("debugger.js this.breakContext "+this.breakContext.getName());
  727.         return true;
  728.     },
  729.  
  730.     injectConsole: function(context, frameWin)
  731.     {
  732.         if (context.notificationSourceFile)
  733.         {
  734.             delete context.sourceFileMap[context.notificationSourceFile.href];
  735.             delete context.notificationSourceFile;
  736.         }
  737.         if (Firebug.Console.isAlwaysEnabled())
  738.         {
  739.             // This is how the console is injected ahead of JS running on the page
  740.             fbs.filterConsoleInjections = true;
  741.             try
  742.             {
  743.                 var consoleReady = Firebug.Console.isReadyElsePreparing(context, frameWin);
  744.             }
  745.             catch(exc)
  746.             {
  747.             }
  748.             finally
  749.             {
  750.                 fbs.filterConsoleInjections = false;
  751.             }
  752.         }
  753.         else
  754.         {
  755.         }
  756.     },
  757.  
  758.     onLock: function(state)
  759.     {
  760.         // XXXjoe For now, trying to see if it's ok to have multiple contexts
  761.         // debugging simultaneously - otherwise we need this
  762.         //if (this.context != this.debugContext)
  763.         {
  764.             // XXXjoe Disable step/continue buttons
  765.         }
  766.     },
  767.  
  768.     onBreak: function(frame, type)
  769.     {
  770.         try {
  771.             var context = this.breakContext;
  772.  
  773.             if (!context)
  774.                 context = this.getContextByFrame(frame);
  775.  
  776.             delete this.breakContext;
  777.  
  778.             if (!context)
  779.                 return RETURN_CONTINUE;
  780.  
  781.             if (type == TYPE_DEBUGGER_KEYWORD && frame.functionName === 'firebugDebuggerTracer')
  782.             {
  783.                 var trace = FBL.getCorrectedStackTrace(frame, context);
  784.                 if (trace)
  785.                 {
  786.                     trace.frames = trace.frames.slice(1).reverse(); // drop the firebugDebuggerTracer and reorder
  787.                     Firebug.Console.log(trace, context, "stackTrace");
  788.                 }
  789.  
  790.                 return RETURN_CONTINUE;
  791.             }
  792.  
  793.             return this.stop(context, frame, type);
  794.         }
  795.         catch (exc)
  796.         {
  797.             throw exc;
  798.         }
  799.     },
  800.  
  801.     onHalt: function(frame)
  802.     {
  803.         var callback = this.haltCallback;
  804.         delete this.haltCallback;
  805.  
  806.         if (callback)
  807.             callback(frame);
  808.  
  809.         return RETURN_CONTINUE;
  810.     },
  811.  
  812.     onThrow: function(frame, rv)
  813.     {
  814.         // onThrow is called for throw and for any catch that does not succeed.
  815.         var context = this.breakContext;
  816.         delete this.breakContext;
  817.  
  818.         if (!context)
  819.         {
  820.             FBTrace.sysout("debugger.onThrow, no context, try to get from frame\n");
  821.             context = this.getContextByFrame(frame);
  822.         }
  823.         if (!context)
  824.             return RETURN_CONTINUE_THROW;
  825.  
  826.         if (!fbs.trackThrowCatch)
  827.             return RETURN_CONTINUE_THROW;
  828.  
  829.         try
  830.         {
  831.             var isCatch = this.isCatchFromPreviousThrow(frame, context);
  832.             if (!isCatch)
  833.             {
  834.                 context.thrownStackTrace = getCorrectedStackTrace(frame, context);
  835.             }
  836.             else
  837.             {
  838.             }
  839.         }
  840.         catch  (exc)
  841.         {
  842.             FBTrace.sysout("onThrow FAILS: "+exc+"\n");
  843.         }
  844.  
  845.         if (dispatch2(this.fbListeners,"onThrow",[context, frame, rv]))
  846.             return this.stop(context, frame, TYPE_THROW, rv);
  847.         return RETURN_CONTINUE_THROW;
  848.     },
  849.  
  850.     isCatchFromPreviousThrow: function(frame, context)
  851.     {
  852.         if (context.thrownStackTrace)
  853.         {
  854.             var trace = context.thrownStackTrace.frames;
  855.             if (trace.length > 1)  // top of stack is [0]
  856.             {
  857.                 var curFrame = frame;
  858.                 var curFrameSig = curFrame.script.tag +"."+curFrame.pc;
  859.                 for (var i = 1; i < trace.length; i++)
  860.                 {
  861.                     var preFrameSig = trace[i].signature();
  862.                     if (curFrameSig == preFrameSig)
  863.                     {
  864.                         return true;  // catch from previous throw (or do we need to compare whole stack?
  865.                     }
  866.                 }
  867.                 // We looked at the previous stack and did not match the current frame
  868.             }
  869.         }
  870.        return false;
  871.     },
  872.  
  873.     onMonitorScript: function(frame)
  874.     {
  875.         var context = this.breakContext;
  876.         delete this.breakContext;
  877.  
  878.         if (!context)
  879.             context = this.getContextByFrame(frame);
  880.         if (!context)
  881.             return RETURN_CONTINUE;
  882.  
  883.         frame = getStackFrame(frame, context);
  884.  
  885.         dispatch(this.fbListeners,"onMonitorScript",[context, frame]);
  886.     },
  887.  
  888.     onFunctionCall: function(context, frame, depth, calling)
  889.     {
  890.         if (!context)
  891.             context = this.getContextByFrame(frame);
  892.         if (!context)
  893.             return RETURN_CONTINUE;
  894.  
  895.         frame = getStackFrame(frame, context);
  896.  
  897.         dispatch(this.fbListeners,"onFunctionCall",[context, frame, depth, calling]);
  898.  
  899.         return context;  // returned as first arg on next call from same trace
  900.     },
  901.  
  902.     onError: function(frame, error)
  903.     {
  904.         var context = this.breakContext;
  905.         delete this.breakContext;
  906.  
  907.         try
  908.         {
  909.             if (reTooMuchRecursion.test(error.errorMessage))
  910.                 frame = fbs.discardRecursionFrames(frame);
  911.  
  912.             Firebug.errorStackTrace = getCorrectedStackTrace(frame, context);
  913.             if (Firebug.breakOnErrors)
  914.             {
  915.                 context.breakingCause = {
  916.                     title: $STR("Break on Error"),
  917.                     message: error.message,
  918.                     copyAction: bindFixed(FirebugReps.ErrorMessage.copyError,
  919.                         FirebugReps.ErrorMessage, error)
  920.                 };
  921.             }
  922.             else
  923.             {
  924.                 delete context.breakingCause;
  925.             }
  926.         }
  927.         catch (exc)
  928.         {
  929.         }
  930.  
  931.         var hookReturn = dispatch2(this.fbListeners,"onError",[context, frame, error]);
  932.  
  933.         if (Firebug.breakOnErrors)
  934.         {
  935.             //xxxHonza: for now, the BON options are disabled to keep things simple.
  936.             // Deactivate "Break On All Errors" only if the other options says so.
  937.             //if (!Firebug.persistBreakOnError)
  938.             //    Firebug.setPref(Firebug.servicePrefDomain, "breakOnErrors", false);
  939.  
  940.             // Switch of Break on Next tab lightning.
  941.             var panel = context.getPanel("console", true);
  942.             //Firebug.Breakpoint.updatePanelTab(panel, false);
  943.  
  944.             return -1;  // break
  945.         }
  946.  
  947.         if (hookReturn)
  948.             return hookReturn;
  949.  
  950.         return -2; /* let firebug service decide to break or not */
  951.     },
  952.  
  953.     onUncaughtException: function(errorInfo)
  954.     {
  955.         var context = this.breakContext;
  956.         delete this.breakContext;
  957.  
  958.         Firebug.Errors.logScriptError(context, errorInfo, false);
  959.         return -2;
  960.     },
  961.  
  962.     onEvalScriptCreated: function(frame, outerScript, innerScripts)
  963.     {
  964.         try
  965.         {
  966.             var context = this.breakContext;
  967.             delete this.breakContext;
  968.  
  969.             var sourceFile = this.getEvalLevelSourceFile(frame, context, innerScripts);
  970.  
  971.             dispatch(this.fbListeners,"onEvalScriptCreated",[context, frame, sourceFile.href]);
  972.             return sourceFile;
  973.         }
  974.         catch (e)
  975.         {
  976.         }
  977.     },
  978.  
  979.     onEventScriptCreated: function(frame, outerScript, innerScripts)
  980.     {
  981.         var context = this.breakContext;
  982.         delete this.breakContext;
  983.  
  984.         var script = frame.script;
  985.         var creatorURL = normalizeURL(frame.script.fileName);
  986.         var innerScriptArray = [];
  987.         try {
  988.             var source = script.functionSource;
  989.  
  990.             while (innerScripts.hasMoreElements())
  991.             {
  992.                 var inner = innerScripts.getNext();
  993.                 source += "\n"+inner.functionSource;
  994.                 innerScriptArray.push(inner);
  995.             }
  996.  
  997.         } catch (exc) {
  998.             /*Bug 426692 */
  999.             var source = creatorURL + "/"+getUniqueId();
  1000.         }
  1001.  
  1002.         var lines = splitLines(source);
  1003.  
  1004.         var urlDescribed = this.getDynamicURL(context, normalizeURL(frame.script.fileName), source, "event");
  1005.         var url = urlDescribed.href;
  1006.  
  1007.         context.sourceCache.invalidate(url);
  1008.         context.sourceCache.storeSplitLines(url, lines);
  1009.  
  1010.         var sourceFile = new Firebug.EventSourceFile(url, frame.script, "event:"+script.functionName+"."+script.tag, lines, new ArrayEnumerator(innerScriptArray));
  1011.         this.watchSourceFile(context, sourceFile);
  1012.  
  1013.         dispatch(this.fbListeners,"onEventScriptCreated",[context, frame, url]);
  1014.         return sourceFile;
  1015.     },
  1016.  
  1017.     // We just compiled a bunch of JS, eg a script tag in HTML.  We are about to run the outerScript.
  1018.     onTopLevelScriptCreated: function(frame, outerScript, innerScripts)
  1019.     {
  1020.         var context = this.breakContext;
  1021.         delete this.breakContext;
  1022.  
  1023.         // This is our only chance to get the linetable for the outerScript since it will run and be GC next.
  1024.         var script = frame.script;
  1025.         var url = normalizeURL(script.fileName);
  1026.  
  1027.         var sourceFile = context.sourceFileMap[url];
  1028.         if (sourceFile && (sourceFile instanceof Firebug.TopLevelSourceFile) )      // TODO test multiple script tags in one html file
  1029.         {
  1030.             if (!sourceFile.outerScript || !sourceFile.outerScript.isValid)
  1031.                 sourceFile.outerScript = outerScript;
  1032.             Firebug.SourceFile.addScriptsToSourceFile(sourceFile, outerScript, innerScripts);
  1033.         }
  1034.         else
  1035.         {
  1036.             sourceFile = new Firebug.TopLevelSourceFile(url, script, script.lineExtent, innerScripts);
  1037.             this.watchSourceFile(context, sourceFile);
  1038.         }
  1039.  
  1040.         dispatch(this.fbListeners,"onTopLevelScriptCreated",[context, frame, sourceFile.href]);
  1041.         return sourceFile;
  1042.     },
  1043.  
  1044.     getContextByFrame: function(frame)
  1045.     {
  1046.         var win = getFrameScopeWindowAncestor(frame);
  1047.         return win ? TabWatcher.getContextByWindow(win) : null;
  1048.     },
  1049.  
  1050.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1051.  
  1052.     watchSourceFile: function(context, sourceFile)
  1053.     {
  1054.         context.addSourceFile(sourceFile);  // store in the context and notify listeners
  1055.         //fbs.watchSourceFile(sourceFile);    // tell the service to watch this file
  1056.     },
  1057.  
  1058.     unwatchSourceFile: function(context, sourceFile)
  1059.     {
  1060.         //fbs.unwatchSourceFile(sourceFile);
  1061.         context.removeSourceFile(sourceFile);
  1062.     },
  1063.  
  1064.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1065.  
  1066.     onToggleBreakpoint: function(url, lineNo, isSet, props)
  1067.     {
  1068.         if (props.debugger != this) // then not for us
  1069.         {
  1070.             return;
  1071.         }
  1072.  
  1073.         for (var i = 0; i < TabWatcher.contexts.length; ++i)
  1074.         {
  1075.             var context = TabWatcher.contexts[i];
  1076.             var sourceFile = context.sourceFileMap[url];
  1077.             if (sourceFile) {
  1078.                 if (!isSet && context.dynamicURLhasBP)
  1079.                     this.checkDynamicURLhasBP(context);
  1080.  
  1081.                 var panel = context.getPanel("script", true);
  1082.                 if (!panel)
  1083.                 {
  1084.                     return;
  1085.                 }
  1086.  
  1087.                 panel.context.invalidatePanels("breakpoints");
  1088.  
  1089.                 var sourceBox = panel.getSourceBoxByURL(url);
  1090.                 if (!sourceBox)
  1091.                 {
  1092.                 }
  1093.  
  1094.                 var row = sourceBox.getLineNode(lineNo);
  1095.                 if (!row)
  1096.                     continue;  // we *should* only be called for lines in the viewport...
  1097.  
  1098.                 row.setAttribute("breakpoint", isSet);
  1099.                 if (isSet && props)
  1100.                 {
  1101.                     row.setAttribute("condition", props.condition ? "true" : "false");
  1102.                     if (props.condition)  // issue 1371
  1103.                     {
  1104.                         var watchPanel = this.ableWatchSidePanel(context);
  1105.                         watchPanel.addWatch(props.condition);
  1106.                     }
  1107.                     row.setAttribute("disabledBreakpoint", new Boolean(props.disabled).toString());
  1108.                 }
  1109.                 else
  1110.                 {
  1111.                     row.removeAttribute("condition");
  1112.                     if (props.condition)
  1113.                     {
  1114.                         var watchPanel = this.ableWatchSidePanel(context);
  1115.                         watchPanel.removeWatch(props.condition);
  1116.                         watchPanel.rebuild();
  1117.                     }
  1118.                     row.removeAttribute("disabledBreakpoint");
  1119.                 }
  1120.                 dispatch(this.fbListeners, "onToggleBreakpoint", [context, url, lineNo, isSet]);
  1121.                 return;
  1122.             }
  1123.         }
  1124.     },
  1125.  
  1126.     onToggleErrorBreakpoint: function(url, lineNo, isSet)
  1127.     {
  1128.         for (var i = 0; i < TabWatcher.contexts.length; ++i)
  1129.         {
  1130.             var context = TabWatcher.contexts[i];
  1131.             var panel = context.getPanel("console", true);
  1132.             if (panel)
  1133.             {
  1134.                 panel.context.invalidatePanels("breakpoints");
  1135.  
  1136.                 for (var row = panel.panelNode.firstChild; row; row = row.nextSibling)
  1137.                 {
  1138.                     var error = row.firstChild.repObject;
  1139.                     if (error instanceof ErrorMessage && error.href == url && error.lineNo == lineNo)
  1140.                     {
  1141.                         if (isSet)
  1142.                             setClass(row.firstChild, "breakForError");
  1143.                         else
  1144.                             removeClass(row.firstChild, "breakForError");
  1145.  
  1146.                         dispatch(this.fbListeners, "onToggleErrorBreakpoint", [context, url, lineNo, isSet]);
  1147.                     }
  1148.                 }
  1149.             }
  1150.         }
  1151.     },
  1152.  
  1153.     onToggleMonitor: function(url, lineNo, isSet)
  1154.     {
  1155.         for (var i = 0; i < TabWatcher.contexts.length; ++i)
  1156.         {
  1157.             var panel = TabWatcher.contexts[i].getPanel("console", true);
  1158.             if (panel)
  1159.                 panel.context.invalidatePanels("breakpoints");
  1160.         }
  1161.     },
  1162.  
  1163.     checkDynamicURLhasBP: function (context)
  1164.     {
  1165.         context.dynamicURLhasBP = false;
  1166.         for (var url in context.sourceFileMap)
  1167.         {
  1168.              var sourceFile = context.sourceFileMap[url];
  1169.                if (sourceFile.isEval() || sourceFile.isEvent())
  1170.                {
  1171.                    fbs.enumerateBreakpoints(url, {call: function setDynamicIfSet(url, lineNo)
  1172.                    {
  1173.                        context.dynamicURLhasBP = true;
  1174.                    }});
  1175.                }
  1176.                if (context.dynamicURLhasBP)
  1177.                    break;
  1178.         }
  1179.     },
  1180.  
  1181.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1182.     // XXXjjb this code is not called, because I found the scheme for detecting Function too complex.
  1183.     // I'm leaving it here to remind us that we need to support new Function().
  1184.     onFunctionConstructor: function(frame, ctor_script)
  1185.     {
  1186.        try
  1187.         {
  1188.             var context = this.breakContext;
  1189.             delete this.breakContext;
  1190.  
  1191.             var sourceFile = this.createSourceFileForFunctionConstructor(frame, ctor_script, context);
  1192.  
  1193.             dispatch(this.fbListeners,"onFunctionConstructor",[context, frame, ctor_script, sourceFile.href]);
  1194.             return sourceFile.href;
  1195.         }
  1196.         catch(exc)
  1197.         {
  1198.             ERROR("debugger.onFunctionConstructor failed: "+exc);
  1199.             return null;
  1200.         }
  1201.  
  1202.     },
  1203.  
  1204.     createSourceFileForFunctionConstructor: function(caller_frame, ctor_script, context)
  1205.     {
  1206.         var ctor_expr = null; // this.getConstructorExpression(caller_frame, context);
  1207.         if (ctor_expr)
  1208.             var source  = this.getEvalBody(caller_frame, "lib.createSourceFileForFunctionConstructor ctor_expr", 1, ctor_expr);
  1209.         else
  1210.             var source = " bah createSourceFileForFunctionConstructor"; //ctor_script.functionSource;
  1211.  
  1212.         var url = this.getDynamicURL(context, normalizeURL(caller_frame.script.fileName), source, "Function");
  1213.  
  1214.         var lines = context.sourceCache.store(url.href, source);
  1215.         var sourceFile = new Firebug.FunctionConstructorSourceFile(url, caller_frame.script, ctor_expr, lines.length);
  1216.         this.watchSourceFile(context, sourceFile);
  1217.  
  1218.         return sourceFile;
  1219.     },
  1220.  
  1221.     getConstructorExpression: function(caller_frame, context)
  1222.     {
  1223.         // We believe we are just after the ctor call.
  1224.         var decompiled_lineno = getLineAtPC(caller_frame, context);
  1225.         var decompiled_lines = splitLines(caller_frame.script.functionSource);  // TODO place in sourceCache?
  1226.         var candidate_line = decompiled_lines[decompiled_lineno - 1]; // zero origin
  1227.         if (candidate_line && candidate_line != null)
  1228.             {
  1229.                 var m = reFunction.exec(candidate_line);
  1230.                 if (m)
  1231.                     var arguments =  m[1];     // TODO Lame: need to count parens, with escapes and quotes
  1232.             }
  1233.         if (arguments) // need to break down commas and get last arg.
  1234.         {
  1235.                 var lastComma = arguments.lastIndexOf(',');
  1236.                 return arguments.substring(lastComma+1);  // if -1 then 0
  1237.         }
  1238.         return null;
  1239.     },
  1240.     // end of guilt trip
  1241.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1242.  
  1243.     // Called by debugger.onEval() to store eval() source.
  1244.     // The frame has the blank-function-name script and it is not the top frame.
  1245.     // The frame.script.fileName is given by spidermonkey as file of the first eval().
  1246.     // The frame.script.baseLineNumber is given by spidermonkey as the line of the first eval() call
  1247.     // The source that contains the eval() call is the source of our caller.
  1248.     // If our caller is a file, the source of our caller is at frame.script.baseLineNumber
  1249.     // If our caller is an eval, the source of our caller is TODO Check Test Case
  1250.     getEvalLevelSourceFile: function(frame, context, innerScripts)
  1251.     {
  1252.         var eval_expr = this.getEvalExpression(frame, context);
  1253.         if (eval_expr && !Firebug.decompileEvals)
  1254.         {
  1255.             var source  = this.getEvalBody(frame, "lib.getEvalLevelSourceFile.getEvalBody", 1, eval_expr);
  1256.             var mapType = PCMAP_SOURCETEXT;
  1257.         }
  1258.         else
  1259.         {
  1260.             var source = frame.script.functionSource; // XXXms - possible crash on OSX FF2
  1261.             var mapType = PCMAP_PRETTYPRINT;
  1262.         }
  1263.  
  1264.         var lines = splitLines(source);
  1265.  
  1266.         var url = this.getDynamicURL(context, normalizeURL(frame.script.fileName), lines, "eval");
  1267.  
  1268.         context.sourceCache.invalidate(url.href);
  1269.         context.sourceCache.storeSplitLines(url.href, lines);
  1270.  
  1271.         var sourceFile = new Firebug.EvalLevelSourceFile(url, frame.script, eval_expr, lines, mapType, innerScripts);
  1272.         this.watchSourceFile(context, sourceFile);
  1273.  
  1274.         return sourceFile;
  1275.     },
  1276.  
  1277.     getDynamicURL: function(context, callerURL, lines, kind)
  1278.     {
  1279.         var url = this.getURLFromLastLine(context, lines);
  1280.         if (url)
  1281.             return url;
  1282.  
  1283.         var url = this.getSequentialURL(context, callerURL, kind);
  1284.         if (url)
  1285.             return url;
  1286.  
  1287.         var url = this.getURLFromMD5(callerURL, lines, kind);
  1288.         if (url)
  1289.             return url;
  1290.  
  1291.         var url = this.getDataURLForScript(callerURL, lines);
  1292.         if (url)
  1293.             return url;
  1294.  
  1295.         return url;
  1296.     },
  1297.  
  1298.     getURLFromLastLine: function(context, lines)
  1299.     {
  1300.         var url = null;
  1301.         // Ignores any trailing whitespace in |source|
  1302.         const reURIinComment = /\/\/@\ssourceURL=\s*(\S*?)\s*$/m;
  1303.         var m = reURIinComment.exec(lines[lines.length - 1]);
  1304.         if (m)
  1305.         {
  1306.             // add context info to the sourceURL so eval'd sources are grouped correctly in the source file list
  1307.             if (m[1] && m[1].indexOf('://') == -1) {
  1308.                 var loc = context.window.location;
  1309.                 if (m[1].charAt(0) != '/') m[1] = '/'+m[1]; // prepend leading slash if necessary
  1310.                 m[1] = loc.protocol + '//' + loc.host + m[1]; // prepend protocol and host
  1311.             }
  1312.  
  1313.             var href = new String(m[1]);
  1314.  
  1315.             url = {href: href, kind: "source"};
  1316.         }
  1317.         else
  1318.         {
  1319.         }
  1320.         return url;
  1321.     },
  1322.  
  1323.     getSequentialURL: function(context, callerURL, kind)
  1324.     {
  1325.         var url = null;
  1326.         if (!context.dynamicURLhasBP)
  1327.         {
  1328.             // If no breakpoints live in dynamic code then we don't need to compare
  1329.             // the previous and reloaded source. In that case let's use a cheap URL.
  1330.             var href = new String(callerURL + (kind ? "/"+kind+"/" : "/nokind/")+"seq/" +(context.dynamicURLIndex++));
  1331.             url = {href: href, kind: "seq"};
  1332.         }
  1333.         return url;
  1334.     },
  1335.  
  1336.     getURLFromMD5: function(callerURL, lines, kind)
  1337.     {
  1338.         this.hash_service.init(this.nsICryptoHash.MD5);
  1339.         var source = lines.join('\n'); // we could double loop, would that be any faster?
  1340.         byteArray = [];
  1341.         for (var j = 0; j < source.length; j++)
  1342.         {
  1343.             byteArray.push( source.charCodeAt(j) );
  1344.         }
  1345.         this.hash_service.update(byteArray, byteArray.length);
  1346.         var hash = this.hash_service.finish(true);
  1347.  
  1348.         // encoding the hash should be ok, it should be information-preserving? Or at least reversable?
  1349.         var href= new String(callerURL + (kind ? "/"+kind+"/" : "/nokind/")+"MD5/" + encodeURIComponent(hash));
  1350.         url = {href: href, kind: "MD5"};
  1351.         return url;
  1352.     },
  1353.  
  1354.     getDataURLForScript: function(callerURL, lines)
  1355.     {
  1356.         var url = null;
  1357.         var href = null;
  1358.         if (!source)
  1359.             href = "eval."+script.tag;
  1360.         else
  1361.         {
  1362.             // data:text/javascript;fileName=x%2Cy.js;baseLineNumber=10,<the-url-encoded-data>
  1363.             href = new String("data:text/javascript;");
  1364.             href += "fileName="+encodeURIComponent(callerURL);
  1365.             var source = lines.join('\n');
  1366.             //url +=  ";"+ "baseLineNumber="+encodeURIComponent(script.baseLineNumber) +
  1367.             href +="," + encodeURIComponent(source);
  1368.         }
  1369.         url = {href:href, kind:"data"};
  1370.         return url;
  1371.     },
  1372.  
  1373.     // ********************************************************************************
  1374.     getEvalExpression: function(frame, context)
  1375.     {
  1376.         var expr = this.getEvalExpressionFromEval(frame, context);  // eval in eval
  1377.  
  1378.         return (expr) ? expr : this.getEvalExpressionFromFile(normalizeURL(frame.script.fileName), frame.script.baseLineNumber, context);
  1379.     },
  1380.  
  1381.     getEvalExpressionFromFile: function(url, lineNo, context)
  1382.     {
  1383.         if (context && context.sourceCache)
  1384.         {
  1385.             var in_url = FBL.reJavascript.exec(url);
  1386.             if (in_url)
  1387.             {
  1388.                 var m = reEval.exec(in_url[1]);
  1389.                 if (m)
  1390.                     return m[1];
  1391.                 else
  1392.                     return null;
  1393.             }
  1394.  
  1395.             var htm = reHTM.exec(url);
  1396.             if (htm) {
  1397.                 lineNo = lineNo + 1; // embedded scripts seem to be off by one?  XXXjjb heuristic
  1398.             }
  1399.             // Walk backwards from the first line in the function until we find the line which
  1400.             // matches the pattern above, which is the eval call
  1401.             var line = "";
  1402.             for (var i = 0; i < 3; ++i)
  1403.             {
  1404.                 line = context.sourceCache.getLine(url, lineNo-i) + line;
  1405.                 if (line && line != null)
  1406.                 {
  1407.                     var m = reEval.exec(line);
  1408.                     if (m)
  1409.                         return m[1];
  1410.                 }
  1411.             }
  1412.         }
  1413.         return null;
  1414.     },
  1415.  
  1416.     getEvalExpressionFromEval: function(frame, context)
  1417.     {
  1418.         var callingFrame = frame.callingFrame;
  1419.         var sourceFile = Firebug.SourceFile.getSourceFileByScript(context, callingFrame.script);
  1420.         if (sourceFile)
  1421.         {
  1422.             var lineNo = callingFrame.script.pcToLine(callingFrame.pc, PCMAP_SOURCETEXT);
  1423.             lineNo = lineNo - callingFrame.script.baseLineNumber + 1;
  1424.             var url  = sourceFile.href;
  1425.  
  1426.             var line = "";
  1427.             for (var i = 0; i < 3; ++i)
  1428.             {
  1429.                 line = context.sourceCache.getLine(url, lineNo-i) + line;
  1430.                 if (line && line != null)
  1431.                 {
  1432.                     var m = reEval.exec(line);
  1433.                     if (m)
  1434.                         return m[1];     // TODO Lame: need to count parens, with escapes and quotes
  1435.                 }
  1436.             }
  1437.         }
  1438.         return null;
  1439.     },
  1440.  
  1441.     getEvalBody: function(frame, asName, asLine, evalExpr)
  1442.     {
  1443.         if (evalExpr  && !Firebug.decompileEvals)
  1444.         {
  1445.             var result_src = {};
  1446.             var evalThis = "new String("+evalExpr+");";
  1447.             var evaled = frame.eval(evalThis, asName, asLine, result_src);
  1448.  
  1449.             if (evaled)
  1450.             {
  1451.                 var src = unwrapIValue(result_src.value);
  1452.                 return src;
  1453.             }
  1454.             else
  1455.             {
  1456.                 var source;
  1457.                 if(evalExpr == "function(p,a,c,k,e,r")
  1458.                     source = "/packer/ JS compressor detected";
  1459.                 else
  1460.                     source = frame.script.functionSource;
  1461.                 return source+" /* !eval("+evalThis+")) */";
  1462.             }
  1463.         }
  1464.         else
  1465.         {
  1466.             return frame.script.functionSource; // XXXms - possible crash on OSX FF2
  1467.         }
  1468.     },
  1469.  
  1470.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1471.     // extends Module
  1472.  
  1473.     initialize: function()
  1474.     {
  1475.         this.nsICryptoHash = Components.interfaces["nsICryptoHash"];
  1476.  
  1477.         this.debuggerName =  window.location.href+"--"+FBL.getUniqueId(); /*@explore*/
  1478.         this.toString = function() { return this.debuggerName; } /*@explore*/
  1479.         this.hash_service = CCSV("@mozilla.org/security/hash;1", "nsICryptoHash");
  1480.  
  1481.         $("cmd_breakOnErrors").setAttribute("checked", Firebug.breakOnErrors);
  1482.         $("cmd_decompileEvals").setAttribute("checked", Firebug.decompileEvals);
  1483.  
  1484.         this.wrappedJSObject = this;  // how we communicate with fbs
  1485.         this.panelName = "script";
  1486.  
  1487.         // This is a service operation, a way of encapsulating fbs which is in turn implementing this
  1488.         // simple service. We could implment a whole component for this service, but it hardly makes sense.
  1489.         Firebug.broadcast = function encapsulateFBSBroadcast(message, args)
  1490.         {
  1491.             fbs.broadcast(message, args);
  1492.         }
  1493.  
  1494.         this.onFunctionCall = bind(this.onFunctionCall, this);
  1495.         Firebug.ActivableModule.initialize.apply(this, arguments);
  1496.     },
  1497.  
  1498.     /*
  1499.      * per-XUL window registration; this method just allows us to keep fbs in this file.
  1500.      * @param clientAPI an object that implements functions called by fbs for clients.
  1501.      */
  1502.     registerClient: function(clientAPI)
  1503.     {
  1504.         return fbs.registerClient(clientAPI);
  1505.     },
  1506.  
  1507.     unregisterClient: function(clientAPI)
  1508.     {
  1509.         fbs.unregisterClient(clientAPI);
  1510.     },
  1511.  
  1512.     enable: function()
  1513.     {
  1514.         if (this.isAlwaysEnabled())
  1515.             this.registerDebugger(); // allow callbacks for jsd
  1516.     },
  1517.  
  1518.     disable: function()
  1519.     {
  1520.         this.unregisterDebugger();
  1521.     },
  1522.  
  1523.     initializeUI: function()
  1524.     {
  1525.         Firebug.ActivableModule.initializeUI.apply(this, arguments);
  1526.         this.filterButton = $("fbScriptFilterMenu");
  1527.         this.filterMenuUpdate();
  1528.     },
  1529.  
  1530.     initContext: function(context, persistedState)
  1531.     {
  1532.         if (persistedState)
  1533.             context.dynamicURLhasBP = persistedState.dynamicURLhasBP;
  1534.  
  1535.         context.dynamicURLIndex = 1; // any dynamic urls need to be unique to the context.
  1536.  
  1537.         Firebug.ActivableModule.initContext.apply(this, arguments);
  1538.     },
  1539.  
  1540.     reattachContext: function(browser, context)
  1541.     {
  1542.         this.filterButton = Firebug.chrome.$("fbScriptFilterMenu");  // connect to the button in the new window, not 'window'
  1543.         this.filterMenuUpdate();
  1544.         Firebug.ActivableModule.reattachContext.apply(this, arguments);
  1545.     },
  1546.  
  1547.     loadedContext: function(context)
  1548.     {
  1549.         var watchPanel = this.ableWatchSidePanel(context);
  1550.         var needNow = watchPanel && watchPanel.watches;
  1551.         var watchPanelState = Firebug.getPanelState({name: "watches", context: context});
  1552.         var needPersistent = watchPanelState && watchPanelState.watches;
  1553.         if (needNow || needPersistent)
  1554.         {
  1555.             Firebug.CommandLine.isReadyElsePreparing(context);
  1556.             if (watchPanel)
  1557.             {
  1558.                 context.setTimeout(function refreshWatchesAfterCommandLineReady()
  1559.                 {
  1560.                     watchPanel.refresh();
  1561.                 });
  1562.             }
  1563.         }
  1564.  
  1565.     },
  1566.  
  1567.     unwatchWindow: function(context, win)  // clean up the source file map in case the frame is being reloaded.
  1568.     {
  1569.         var scriptTags = win.document.getElementsByTagName("script");
  1570.         for (var i = 0; i < scriptTags.length; i++)
  1571.         {
  1572.             var src = scriptTags[i].getAttribute("src");
  1573.             if (src)
  1574.                 delete context.sourceFileMap[src];
  1575.             else
  1576.                 delete context.sourceFileMap[safeGetWindowLocation(win)];
  1577.         }
  1578.         if (scriptTags.length > 0)
  1579.             context.invalidatePanels('script');
  1580.     },
  1581.  
  1582.     destroyContext: function(context, persistedState)
  1583.     {
  1584.         Firebug.ActivableModule.destroyContext.apply(this, arguments);
  1585.  
  1586.         if (context.stopped)
  1587.         {
  1588.             TabWatcher.cancelNextLoad = true;  // the abort will call resume, but the nestedEventLoop will continue the load.
  1589.             this.abort(context);
  1590.         }
  1591.  
  1592.         if(persistedState)
  1593.         {
  1594.             if (context.dynamicURLhasBP)
  1595.                 persistedState.dynamicURLhasBP = context.dynamicURLhasBP;
  1596.             else
  1597.                 delete persistedState.dynamicURLhasBP;
  1598.         }
  1599.     },
  1600.  
  1601.     updateOption: function(name, value)
  1602.     {
  1603.         if (name == "breakOnErrors")
  1604.             $("cmd_breakOnErrors").setAttribute("checked", value);
  1605.         else if (name == "decompileEvals")
  1606.             $("cmd_decompileEvals").setAttribute("checked", value);
  1607.     },
  1608.  
  1609.     getObjectByURL: function(context, url)
  1610.     {
  1611.         var sourceFile = getSourceFileByHref(url, context);
  1612.         if (sourceFile)
  1613.             return new SourceLink(sourceFile.href, 0, "js");
  1614.     },
  1615.  
  1616.     shutdown: function()
  1617.     {
  1618.         fbs.unregisterDebugger(this);
  1619.     },
  1620.  
  1621.     registerDebugger: function() // 1.3.1 safe for multiple calls
  1622.     {
  1623.         if (this.registered)
  1624.             return;
  1625.         this.registered = true;
  1626.  
  1627.         var check = fbs.registerDebugger(this);  //  this will eventually set 'jsd' on the statusIcon
  1628.  
  1629.     },
  1630.  
  1631.     unregisterDebugger: function() // 1.3.1 safe for multiple calls
  1632.     {
  1633.         if (!this.registered)
  1634.             return;
  1635.  
  1636.         if (Firebug.Profiler.isProfiling()) // stay registered if we are profiling across a reload.
  1637.             return;
  1638.  
  1639.         var check = fbs.unregisterDebugger(this);
  1640.  
  1641.         this.registered = false;
  1642.  
  1643.     },
  1644.  
  1645.     onSourceFileCreated: function(context, sourceFile)
  1646.     {
  1647.         // This event can come at any time, eg by frame reloads or ajax, so we need to update the display.
  1648.         context.invalidatePanels("script", "breakpoints");
  1649.     },
  1650.  
  1651.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1652.     // extends ActivableModule
  1653.  
  1654.     onPanelEnable: function(panelName)
  1655.     {
  1656.         if (panelName != this.panelName)
  1657.             return;
  1658.  
  1659.         this.registerDebugger();
  1660.  
  1661.         if (FirebugContext && !fbs.isJSDActive())
  1662.             fbs.unPause();
  1663.  
  1664.     },
  1665.  
  1666.     onPanelDisable: function(panelName)
  1667.     {
  1668.         if (panelName != this.panelName)
  1669.             return;
  1670.  
  1671.         if (this.dependents.length > 0)
  1672.         {
  1673.             for(var i = 0; i < this.dependents.length; i++)
  1674.             {
  1675.                 if (this.dependents[i].isAlwaysEnabled())
  1676.                 {
  1677.                     var name = this.dependents[0].dispatchName; // TODO getName() for modules required.
  1678.                     if (FirebugContext)
  1679.                         Firebug.Console.log("Cannot disable the script panel, "+name+" panel requires it", FirebugContext);
  1680.                     return;
  1681.                 }
  1682.             }
  1683.         }
  1684.         // else no dependents enabled:
  1685.         this.unregisterDebugger();
  1686.  
  1687.         this.clearAllBreakpoints();
  1688.     },
  1689.  
  1690.     onDependentModuleChange: function(dependentAddedOrRemoved)
  1691.     {
  1692.         if (this.dependents.length > 0) // then we have dependents now
  1693.         {
  1694.             if (!this.isAlwaysEnabled()) // then we need to enable
  1695.             {
  1696.                 this.setDefaultState(true);
  1697.                 if (FirebugContext)
  1698.                     Firebug.Console.log("enabling javascript debugger to support "+dependentAddedOrRemoved.dispatchName, FirebugContext);
  1699.             }
  1700.         }
  1701.     },
  1702.  
  1703.     onSuspendFirebug: function()
  1704.     {
  1705.         if (!Firebug.Debugger.isAlwaysEnabled())
  1706.             return;
  1707.  
  1708.         var paused = fbs.pause();  // can be called multiple times.
  1709.  
  1710.         if (!paused)  // then we failed to suspend, undo
  1711.             return true;
  1712.  
  1713.         return false;
  1714.     },
  1715.  
  1716.     onResumeFirebug: function()
  1717.     {
  1718.         if (!Firebug.Debugger.isAlwaysEnabled())
  1719.             return;
  1720.  
  1721.         var unpaused = fbs.unPause();
  1722.  
  1723.     },
  1724.  
  1725.     ableWatchSidePanel: function(context)
  1726.     {
  1727.         if (Firebug.Console.isAlwaysEnabled())
  1728.         {
  1729.             var watchPanel = context.getPanel("watches", true);
  1730.             if (watchPanel)
  1731.                 return watchPanel;
  1732.         }
  1733.  
  1734.         return null;
  1735.     },
  1736.  
  1737.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1738.     // Menu in toolbar.
  1739.  
  1740.     onScriptFilterMenuTooltipShowing: function(tooltip, context)
  1741.     {
  1742.     },
  1743.  
  1744.     onScriptFilterMenuCommand: function(event, context)
  1745.     {
  1746.         var menu = event.target;
  1747.         Firebug.setPref(Firebug.servicePrefDomain, "scriptsFilter", menu.value);
  1748.         Firebug.Debugger.filterMenuUpdate();
  1749.     },
  1750.  
  1751.     menuFullLabel:
  1752.     {
  1753.         static: $STR("ScriptsFilterStatic"),
  1754.         evals: $STR("ScriptsFilterEval"),
  1755.         events: $STR("ScriptsFilterEvent"),
  1756.         all: $STR("ScriptsFilterAll"),
  1757.     },
  1758.  
  1759.     menuShortLabel:
  1760.     {
  1761.         static: $STR("ScriptsFilterStaticShort"),
  1762.         evals: $STR("ScriptsFilterEvalShort"),
  1763.         events: $STR("ScriptsFilterEventShort"),
  1764.         all: $STR("ScriptsFilterAllShort"),
  1765.     },
  1766.  
  1767.     onScriptFilterMenuPopupShowing: function(menu, context)
  1768.     {
  1769.         if (this.menuTooltip)
  1770.             this.menuTooltip.fbEnabled = false;
  1771.  
  1772.         var items = menu.getElementsByTagName("menuitem");
  1773.         var value = this.filterButton.value;
  1774.  
  1775.         for (var i=0; i<items.length; i++)
  1776.         {
  1777.             var option = items[i].value;
  1778.             if (!option)
  1779.                 continue;
  1780.  
  1781.             if (option == value)
  1782.                 items[i].setAttribute("checked", "true");
  1783.  
  1784.             items[i].label = Firebug.Debugger.menuFullLabel[option];
  1785.         }
  1786.  
  1787.         return true;
  1788.     },
  1789.  
  1790.     onScriptFilterMenuPopupHiding: function(tooltip, context)
  1791.     {
  1792.         if (this.menuTooltip)
  1793.             this.menuTooltip.fbEnabled = true;
  1794.  
  1795.         return true;
  1796.     },
  1797.  
  1798.     filterMenuUpdate: function()
  1799.     {
  1800.         var value = Firebug.getPref(Firebug.servicePrefDomain, "scriptsFilter");
  1801.         this.filterButton.value = value;
  1802.         this.filterButton.label = this.menuShortLabel[value];
  1803.         this.filterButton.removeAttribute("disabled");
  1804.         this.filterButton.setAttribute("value", value);
  1805.     },
  1806. });
  1807.  
  1808. // ************************************************************************************************
  1809.  
  1810.  
  1811. Firebug.ScriptPanel = function() {};
  1812.  
  1813.  
  1814. /*
  1815.  * object used to markup Javascript source lines.
  1816.  * In the namespace Firebug.ScriptPanel.
  1817.  */
  1818.  
  1819. Firebug.ScriptPanel.decorator = extend(new Firebug.SourceBoxDecorator,
  1820. {
  1821.     decorate: function(sourceBox, sourceFile)
  1822.     {
  1823.         this.markExecutableLines(sourceBox);
  1824.         this.setLineBreakpoints(sourceBox.repObject, sourceBox)
  1825.     },
  1826.  
  1827.     markExecutableLines: function(sourceBox)
  1828.     {
  1829.         var sourceFile = sourceBox.repObject;
  1830.         var lineNo = sourceBox.firstViewableLine;
  1831.         while( lineNode = sourceBox.getLineNode(lineNo) )
  1832.         {
  1833.             var script = sourceFile.scriptsIfLineCouldBeExecutable(lineNo, true);
  1834.  
  1835.             if (script)
  1836.                 lineNode.setAttribute("executable", "true");
  1837.             else
  1838.                 lineNode.removeAttribute("executable");
  1839.  
  1840.             lineNo++;
  1841.         }
  1842.     },
  1843.  
  1844.     setLineBreakpoints: function(sourceFile, sourceBox)
  1845.     {
  1846.         fbs.enumerateBreakpoints(sourceFile.href, {call: function(url, line, props, script)
  1847.         {
  1848.             var scriptRow = sourceBox.getLineNode(line);
  1849.             if (scriptRow)
  1850.             {
  1851.                 scriptRow.setAttribute("breakpoint", "true");
  1852.                 if (props.disabled)
  1853.                     scriptRow.setAttribute("disabledBreakpoint", "true");
  1854.                 if (props.condition)
  1855.                     scriptRow.setAttribute("condition", "true");
  1856.             }
  1857.         }});
  1858.     },
  1859. });
  1860.  
  1861. Firebug.ScriptPanel.prototype = extend(Firebug.SourceBoxPanel,
  1862. {
  1863.     /*
  1864.     * Framework connection
  1865.     */
  1866.     updateSourceBox: function(sourceBox)
  1867.     {
  1868.         if (this.scrollInfo && (this.scrollInfo.location == this.location))
  1869.             this.scrollToLine(this.location, this.scrollInfo.previousCenterLine);
  1870.         delete this.scrollInfo;
  1871.     },
  1872.  
  1873.     /*
  1874.     * Framework connection
  1875.     */
  1876.     getSourceType: function()
  1877.     {
  1878.         return "js";
  1879.     },
  1880.  
  1881.     /*
  1882.      * Framework connection
  1883.      */
  1884.     getDecorator: function(sourceBox)
  1885.     {
  1886.         return Firebug.ScriptPanel.decorator;
  1887.     },
  1888.  
  1889.     initialize: function(context, doc)
  1890.     {
  1891.         this.location = null;
  1892.         Firebug.SourceBoxPanel.initialize.apply(this, arguments);
  1893.     },
  1894.  
  1895.     // *************************************************************************************
  1896.     showFunction: function(fn)
  1897.     {
  1898.         var sourceLink = findSourceForFunction(fn, this.context);
  1899.         if (sourceLink)
  1900.         {
  1901.             this.showSourceLink(sourceLink);
  1902.         }
  1903.         else
  1904.         {
  1905.         }
  1906.     },
  1907.  
  1908.     showSourceLink: function(sourceLink)
  1909.     {
  1910.         var sourceFile = getSourceFileByHref(sourceLink.href, this.context);
  1911.         if (sourceFile)
  1912.         {
  1913.             this.navigate(sourceFile);
  1914.             if (sourceLink.line)
  1915.             {
  1916.                 this.scrollToLine(sourceLink.href, sourceLink.line, this.jumpHighlightFactory(sourceLink.line, this.context));
  1917.                 dispatch([Firebug.A11yModel], "onShowSourceLink", [this, sourceLink.line]);
  1918.             }
  1919.             if (sourceLink == this.selection)  // then clear it so the next link will scroll and highlight.
  1920.                 delete this.selection;
  1921.         }
  1922.     },
  1923.  
  1924.     showStackFrame: function(frame)
  1925.     {
  1926.         if (!frame || (frame && !frame.isValid))
  1927.         {
  1928.             this.showNoStackFrame();
  1929.             return;
  1930.         }
  1931.  
  1932.         this.context.currentFrame = frame;
  1933.         var sourceFile = Firebug.SourceFile.getSourceFileByScript(this.context, this.context.currentFrame.script);
  1934.         if (!sourceFile)
  1935.         {
  1936.             this.showNoStackFrame()
  1937.             return;
  1938.         }
  1939.  
  1940.         this.context.executingSourceFile = sourceFile;
  1941.         this.executionFile = sourceFile;
  1942.         if (this.executionFile)
  1943.         {
  1944.             var url = this.executionFile.href;
  1945.             var analyzer = this.executionFile.getScriptAnalyzer(frame.script);
  1946.             this.executionLineNo = analyzer.getSourceLineFromFrame(this.context, frame);  // TODo implement for each type
  1947.  
  1948.             if (this.context.breakingCause)
  1949.                 this.context.breakingCause.lineNo = this.executionLineNo;
  1950.  
  1951.             this.scrollToLine(url, this.executionLineNo, bind(this.highlightExecutionLine, this) );
  1952.             this.context.throttle(this.updateInfoTip, this);
  1953.             return;
  1954.         }
  1955.         else
  1956.         {
  1957.             this.showNoStackFrame();
  1958.         }
  1959.     },
  1960.  
  1961.     showNoStackFrame: function()
  1962.     {
  1963.         this.executionFile = null;
  1964.         this.executionLineNo = -1;
  1965.  
  1966.         if (this.selectedSourceBox)
  1967.             this.highlightExecutionLine(this.selectedSourceBox);  // clear highlight
  1968.  
  1969.         var panelStatus = Firebug.chrome.getPanelStatusElements();
  1970.         panelStatus.clear(); // clear stack on status bar
  1971.         this.updateInfoTip();
  1972.     },
  1973.  
  1974.     highlightExecutionLine: function(sourceBox)
  1975.     {
  1976.         if (this.executionLine)  // could point to any node in any sourcebox
  1977.             this.executionLine.removeAttribute("exe_line");
  1978.  
  1979.         var lineNode = sourceBox.getLineNode(this.executionLineNo);
  1980.  
  1981.         this.executionLine = lineNode;  // if null, clears
  1982.  
  1983.         if (sourceBox.breakCauseBox)
  1984.         {
  1985.             sourceBox.breakCauseBox.hide();
  1986.             delete sourceBox.breakCauseBox;
  1987.         }
  1988.  
  1989.         if (lineNode)
  1990.         {
  1991.             lineNode.setAttribute("exe_line", "true");
  1992.             if (this.context.breakingCause && !this.context.breakingCause.shown)
  1993.             {
  1994.                 this.context.breakingCause.shown = true;
  1995.                 var cause = this.context.breakingCause;
  1996.                 if (cause)
  1997.                 {
  1998.                     var sourceLine = getChildByClass(lineNode, "sourceLine");
  1999.                     sourceBox.breakCauseBox = new Firebug.Breakpoint.BreakNotification(this.document, cause);
  2000.                     sourceBox.breakCauseBox.show(sourceLine, this, "not an editor, yet?");
  2001.                 }
  2002.             }
  2003.         }
  2004.  
  2005.         return true; // sticky
  2006.     },
  2007.  
  2008.     toggleBreakpoint: function(lineNo)
  2009.     {
  2010.         var lineNode = this.selectedSourceBox.getLineNode(lineNo);
  2011.         if (!this.location && FBTrace.DBG_ERRORS)
  2012.             FBTrace.sysout("toggleBreakpoint no this.location! ", this);
  2013.         if (this.location.href != this.selectedSourceBox.repObject.href && FBTrace.DBG_ERRORS)
  2014.             FBTrace.sysout("toggleBreakpoint this.location != selectedSourceBox ", this);
  2015.  
  2016.         if (lineNode.getAttribute("breakpoint") == "true")
  2017.             fbs.clearBreakpoint(this.location.href, lineNo);
  2018.         else
  2019.             fbs.setBreakpoint(this.location, lineNo, null, Firebug.Debugger);
  2020.     },
  2021.  
  2022.     toggleDisableBreakpoint: function(lineNo)
  2023.     {
  2024.         var lineNode = this.selectedSourceBox.getLineNode(lineNo);
  2025.         if (lineNode.getAttribute("disabledBreakpoint") == "true")
  2026.             fbs.enableBreakpoint(this.location.href, lineNo);
  2027.         else
  2028.             fbs.disableBreakpoint(this.location.href, lineNo);
  2029.     },
  2030.  
  2031.     editBreakpointCondition: function(lineNo)
  2032.     {
  2033.         var sourceRow = this.selectedSourceBox.getLineNode(lineNo);
  2034.         var sourceLine = getChildByClass(sourceRow, "sourceLine");
  2035.         var condition = fbs.getBreakpointCondition(this.location.href, lineNo);
  2036.  
  2037.         Firebug.Editor.startEditing(sourceLine, condition);
  2038.     },
  2039.  
  2040.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2041.  
  2042.     addSelectionWatch: function()
  2043.     {
  2044.         var watchPanel = this.context.getPanel("watches", true);
  2045.         if (watchPanel)
  2046.         {
  2047.             var selection = this.document.defaultView.getSelection();
  2048.             var source = this.getSourceLinesFrom(selection);
  2049.             watchPanel.addWatch(source);
  2050.         }
  2051.     },
  2052.  
  2053.     copySource: function()
  2054.     {
  2055.         var selection = this.document.defaultView.getSelection();
  2056.         var source = this.getSourceLinesFrom(selection);
  2057.         copyToClipboard(source);
  2058.     },
  2059.  
  2060.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2061.  
  2062.     updateInfoTip: function()
  2063.     {
  2064.         var infoTip = this.panelBrowser.infoTip;
  2065.         if (infoTip && this.infoTipExpr)
  2066.             this.populateInfoTip(infoTip, this.infoTipExpr);
  2067.     },
  2068.  
  2069.     populateInfoTip: function(infoTip, expr)
  2070.     {
  2071.         if (!expr || isJavaScriptKeyword(expr))
  2072.             return false;
  2073.  
  2074.         var self = this;
  2075.         // If the evaluate fails, then we report an error and don't show the infoTip
  2076.         Firebug.CommandLine.evaluate(expr, this.context, null, this.context.getGlobalScope(),
  2077.             function success(result, context)
  2078.             {
  2079.                 var rep = Firebug.getRep(result);
  2080.                 var tag = rep.shortTag ? rep.shortTag : rep.tag;
  2081.  
  2082.                 tag.replace({object: result}, infoTip);
  2083.  
  2084.                 Firebug.chrome.contextMenuObject = result;  // for context menu select()
  2085.  
  2086.                 self.infoTipExpr = expr;
  2087.             },
  2088.             function failed(result, context)
  2089.             {
  2090.                 self.infoTipExpr = "";
  2091.             }
  2092.         );
  2093.         return (self.infoTipExpr == expr);
  2094.     },
  2095.  
  2096.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2097.     // UI event listeners
  2098.  
  2099.     onMouseDown: function(event)
  2100.     {
  2101.         // Don't interfere with clicks made into a notification editor.
  2102.         if (getAncestorByClass(event.target, "breakNotification"))
  2103.             return;
  2104.  
  2105.         var sourceLine = getAncestorByClass(event.target, "sourceLine");
  2106.         if (!sourceLine)
  2107.             return;
  2108.  
  2109.         var sourceRow = sourceLine.parentNode;
  2110.         var sourceFile = sourceRow.parentNode.repObject;
  2111.         var lineNo = parseInt(sourceLine.textContent);
  2112.  
  2113.         if (isLeftClick(event))
  2114.             this.toggleBreakpoint(lineNo);
  2115.         else if (isShiftClick(event))
  2116.             this.toggleDisableBreakpoint(lineNo);
  2117.         else if (isControlClick(event) || isMiddleClick(event))
  2118.         {
  2119.             Firebug.Debugger.runUntil(this.context, sourceFile, lineNo, Firebug.Debugger);
  2120.             cancelEvent(event);
  2121.         }
  2122.     },
  2123.  
  2124.     onContextMenu: function(event)
  2125.     {
  2126.         var sourceLine = getAncestorByClass(event.target, "sourceLine");
  2127.         if (!sourceLine)
  2128.             return;
  2129.  
  2130.         var lineNo = parseInt(sourceLine.textContent);
  2131.         this.editBreakpointCondition(lineNo);
  2132.         cancelEvent(event);
  2133.     },
  2134.  
  2135.     onMouseOver: function(event)
  2136.     {
  2137.         var sourceLine = getAncestorByClass(event.target, "sourceLine");
  2138.         if (sourceLine)
  2139.         {
  2140.             if (this.hoveredLine)
  2141.                 removeClass(this.hoveredLine.parentNode, "hovered");
  2142.  
  2143.             this.hoveredLine = sourceLine;
  2144.  
  2145.             if (sourceLine)
  2146.                 setClass(sourceLine.parentNode, "hovered");
  2147.         }
  2148.     },
  2149.  
  2150.     onMouseOut: function(event)
  2151.     {
  2152.         var sourceLine = getAncestorByClass(event.relatedTarget, "sourceLine");
  2153.         if (!sourceLine)
  2154.         {
  2155.             if (this.hoveredLine)
  2156.                 removeClass(this.hoveredLine.parentNode, "hovered");
  2157.  
  2158.             delete this.hoveredLine;
  2159.         }
  2160.     },
  2161.  
  2162.     onScroll: function(event)
  2163.     {
  2164.         var scrollingElement = event.target;
  2165.         this.reView(scrollingElement);
  2166.     },
  2167.  
  2168.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2169.     // extends Panel
  2170.  
  2171.     name: "script",
  2172.     searchable: true,
  2173.     breakable: true,
  2174.  
  2175.     initialize: function(context, doc)
  2176.     {
  2177.         this.onMouseDown = bind(this.onMouseDown, this);
  2178.         this.onContextMenu = bind(this.onContextMenu, this);
  2179.         this.onMouseOver = bind(this.onMouseOver, this);
  2180.         this.onMouseOut = bind(this.onMouseOut, this);
  2181.         this.onScroll = bind(this.onScroll, this);
  2182.  
  2183.         this.panelSplitter = $("fbPanelSplitter");
  2184.         this.sidePanelDeck = $("fbSidePanelDeck");
  2185.  
  2186.         Firebug.SourceBoxPanel.initialize.apply(this, arguments);
  2187.     },
  2188.  
  2189.     destroy: function(state)
  2190.     {
  2191.         delete this.selection; // We want the location (sourcefile) to persist, not the selection (eg stackFrame).
  2192.         persistObjects(this, state);
  2193.  
  2194.         state.location = this.location;
  2195.  
  2196.         var sourceBox = this.selectedSourceBox;
  2197.         if (sourceBox)
  2198.         {
  2199.             state.previousCenterLine = sourceBox.centerLine;
  2200.             delete this.selectedSourceBox;
  2201.         }
  2202.  
  2203.         Firebug.SourceBoxPanel.destroy.apply(this, arguments);
  2204.     },
  2205.  
  2206.     detach: function(oldChrome, newChrome)
  2207.     {
  2208.         if (this.selectedSourceBox)
  2209.             this.lastSourceScrollTop = this.selectedSourceBox.scrollTop;
  2210.  
  2211.         if (this.context.stopped)
  2212.         {
  2213.             Firebug.Debugger.detachListeners(this.context, oldChrome);
  2214.             Firebug.Debugger.attachListeners(this.context, newChrome);
  2215.         }
  2216.  
  2217.         Firebug.Debugger.syncCommands(this.context);
  2218.  
  2219.         Firebug.SourceBoxPanel.detach.apply(this, arguments);
  2220.     },
  2221.  
  2222.     reattach: function(doc)
  2223.     {
  2224.         Firebug.SourceBoxPanel.reattach.apply(this, arguments);
  2225.  
  2226.         setTimeout(bind(function delayScrollToLastTop()
  2227.         {
  2228.             if (this.lastSourceScrollTop)
  2229.             {
  2230.                 this.selectedSourceBox.scrollTop = this.lastSourceScrollTop;
  2231.                 delete this.lastSourceScrollTop;
  2232.             }
  2233.         }, this));
  2234.     },
  2235.  
  2236.     initializeNode: function(oldPanelNode)
  2237.     {
  2238.         this.tooltip = this.document.createElement("div");
  2239.         setClass(this.tooltip, "scriptTooltip");
  2240.         this.tooltip.setAttribute('aria-live', 'polite')
  2241.         obscure(this.tooltip, true);
  2242.         this.panelNode.appendChild(this.tooltip);
  2243.  
  2244.         this.panelNode.addEventListener("mousedown", this.onMouseDown, true);
  2245.         this.panelNode.addEventListener("contextmenu", this.onContextMenu, false);
  2246.         this.panelNode.addEventListener("mouseover", this.onMouseOver, false);
  2247.         this.panelNode.addEventListener("mouseout", this.onMouseOut, false);
  2248.         this.panelNode.addEventListener("scroll", this.onScroll, true);
  2249.         Firebug.SourceBoxPanel.initializeNode.apply(this, arguments);
  2250.         dispatch([Firebug.A11yModel], "onInitializeNode", [this]);
  2251.     },
  2252.  
  2253.     destroyNode: function()
  2254.     {
  2255.         if (this.tooltipTimeout)
  2256.             clearTimeout(this.tooltipTimeout);
  2257.  
  2258.         this.panelNode.removeEventListener("mousedown", this.onMouseDown, true);
  2259.         this.panelNode.removeEventListener("contextmenu", this.onContextMenu, false);
  2260.         this.panelNode.removeEventListener("mouseover", this.onMouseOver, false);
  2261.         this.panelNode.removeEventListener("mouseout", this.onMouseOut, false);
  2262.         this.panelNode.removeEventListener("scroll", this.onScroll, true);
  2263.         Firebug.SourceBoxPanel.destroyNode.apply(this, arguments);
  2264.         dispatch([Firebug.A11yModel], "onDestroyNode", [this]);
  2265.     },
  2266.  
  2267.     clear: function()
  2268.     {
  2269.         clearNode(this.panelNode);
  2270.     },
  2271.  
  2272.     show: function(state)
  2273.     {
  2274.         var enabled = Firebug.Debugger.isAlwaysEnabled();
  2275.  
  2276.         // These buttons are visible only if debugger is enabled.
  2277.         this.showToolbarButtons("fbLocationSeparator", enabled);
  2278.         this.showToolbarButtons("fbDebuggerButtons", enabled);
  2279.         this.showToolbarButtons("fbLocationList", enabled);
  2280.         this.showToolbarButtons("fbScriptButtons", enabled);
  2281.  
  2282.         // Additional debugger panels are visible only if debugger
  2283.         // is enabled.
  2284.         this.panelSplitter.collapsed = !enabled;
  2285.         this.sidePanelDeck.collapsed = !enabled;
  2286.  
  2287.         this.highlight(this.context.stopped);
  2288.  
  2289.         if (enabled)
  2290.         {
  2291.             Firebug.Debugger.disabledPanelPage.hide(this);
  2292.  
  2293.             if (this.context.loaded)
  2294.             {
  2295.                 if (!this.restored)  // this work should be done in loadedContext but on the panel
  2296.                 {
  2297.                     delete this.location;  // remove the default location if any
  2298.                     restoreLocation(this, state);
  2299.                 }
  2300.  
  2301.                 if (state && this.location)  // then we are restoring and we have a location, so scroll when we can
  2302.                     this.scrollInfo = { location: this.location, previousCenterLine: state.previousCenterLine};
  2303.                 if (!this.selectedSourceBox)  // somehow we did not make a sourcebox?
  2304.                     this.navigate(this.location);
  2305.  
  2306.                 this.restored = true;
  2307.             }
  2308.             else // show default
  2309.                 this.navigate(this.location);
  2310.  
  2311.             var breakpointPanel = this.context.getPanel("breakpoints", true);
  2312.             if (breakpointPanel)
  2313.                 breakpointPanel.refresh();
  2314.         }
  2315.         else
  2316.         {
  2317.             Firebug.Debugger.disabledPanelPage.show(this);
  2318.         }
  2319.     },
  2320.  
  2321.     enablePanel: function(module)
  2322.     {
  2323.         Firebug.ActivablePanel.enablePanel.apply(this, arguments);
  2324.  
  2325.         this.panelSplitter.collapsed = false;
  2326.         this.sidePanelDeck.collapsed = false;
  2327.     },
  2328.  
  2329.     disablePanel: function(module)
  2330.     {
  2331.         Firebug.ActivablePanel.disablePanel.apply(this, arguments);
  2332.  
  2333.         this.panelSplitter.collapsed = true;
  2334.         this.sidePanelDeck.collapsed = true;
  2335.     },
  2336.  
  2337.     hide: function(state)
  2338.     {
  2339.         this.showToolbarButtons("fbDebuggerButtons", false);
  2340.  
  2341.         this.highlight(this.context.stopped);
  2342.  
  2343.         this.showToolbarButtons("fbScriptButtons", false);
  2344.         var panelStatus = Firebug.chrome.getPanelStatusElements();
  2345.         FBL.hide(panelStatus, false);
  2346.  
  2347.         delete this.infoTipExpr;
  2348.     },
  2349.  
  2350.     search: function(text, reverse)
  2351.     {
  2352.         var sourceBox = this.selectedSourceBox;
  2353.         if (!text || !sourceBox)
  2354.         {
  2355.             delete this.currentSearch;
  2356.             return false;
  2357.         }
  2358.  
  2359.         // Check if the search is for a line number
  2360.         var m = reLineNumber.exec(text);
  2361.         if (m)
  2362.         {
  2363.             if (!m[1])
  2364.                 return true; // Don't beep if only a # has been typed
  2365.  
  2366.             var lineNo = parseInt(m[1]);
  2367.             if (!isNaN(lineNo) && (lineNo > 0) && (lineNo < sourceBox.lines.length) )
  2368.             {
  2369.                 this.scrollToLine(sourceBox.repObject.href, lineNo,  this.jumpHighlightFactory(lineNo, this.context))
  2370.                 return true;
  2371.             }
  2372.         }
  2373.  
  2374.         var curDoc = this.searchCurrentDoc(!Firebug.searchGlobal, text, reverse);
  2375.         if (!curDoc && Firebug.searchGlobal)
  2376.         {
  2377.             return this.searchOtherDocs(text, reverse);
  2378.         }
  2379.         return curDoc;
  2380.     },
  2381.  
  2382.     searchOtherDocs: function(text, reverse)
  2383.     {
  2384.         var scanRE = Firebug.Search.getTestingRegex(text);
  2385.  
  2386.         var self = this;
  2387.  
  2388.         function scanDoc(sourceFile) {
  2389.             var lines = sourceFile.loadScriptLines(self.context);
  2390.             if (!lines)
  2391.                 return;
  2392.             // we don't care about reverse here as we are just looking for existence,
  2393.             // if we do have a result we will handle the reverse logic on display
  2394.             for (var i = 0; i < lines.length; i++) {
  2395.                 if (scanRE.test(lines[i]))
  2396.                 {
  2397.                     return true;
  2398.                 }
  2399.             }
  2400.         }
  2401.  
  2402.         if (this.navigateToNextDocument(scanDoc, reverse))
  2403.         {
  2404.             return this.searchCurrentDoc(true, text, reverse);
  2405.         }
  2406.     },
  2407.  
  2408.     searchCurrentDoc: function(wrapSearch, text, reverse)
  2409.     {
  2410.         var sourceBox = this.selectedSourceBox;
  2411.  
  2412.         var lineNo = null;
  2413.         if (this.currentSearch && text == this.currentSearch.text)
  2414.             lineNo = this.currentSearch.findNext(wrapSearch, reverse, Firebug.Search.isCaseSensitive(text));
  2415.         else
  2416.         {
  2417.             this.currentSearch = new SourceBoxTextSearch(sourceBox);
  2418.             lineNo = this.currentSearch.find(text, reverse, Firebug.Search.isCaseSensitive(text));
  2419.         }
  2420.  
  2421.         if (lineNo || lineNo === 0)
  2422.         {
  2423.             // this lineNo is an zero-based index into sourceBox.lines. Add one for user line numbers
  2424.             this.scrollToLine(sourceBox.repObject.href, lineNo, this.jumpHighlightFactory(lineNo+1, this.context));
  2425.             dispatch([Firebug.A11yModel], 'onScriptSearchMatchFound', [this, text, sourceBox.repObject, lineNo]);
  2426.  
  2427.             return true;
  2428.         }
  2429.         else
  2430.         {
  2431.             dispatch([Firebug.A11yModel], 'onScriptSearchMatchFound', [this, text, null, null]);
  2432.             return false;
  2433.         }
  2434.     },
  2435.  
  2436.     getSearchOptionsMenuItems: function()
  2437.     {
  2438.         return [
  2439.             Firebug.Search.searchOptionMenu("search.Case_Sensitive", "searchCaseSensitive"),
  2440.             Firebug.Search.searchOptionMenu("search.Multiple_Files", "searchGlobal")
  2441.         ];
  2442.     },
  2443.  
  2444.     supportsObject: function(object)
  2445.     {
  2446.         if( object instanceof jsdIStackFrame
  2447.             || object instanceof Firebug.SourceFile
  2448.             || (object instanceof SourceLink && object.type == "js")
  2449.             || typeof(object) == "function" )
  2450.             return 1;
  2451.         else return 0;
  2452.     },
  2453.  
  2454.     hasObject: function(object)
  2455.     {
  2456.         FBTrace.sysout("debugger.hasObject in "+this.context.getName()+" SourceLink: "+(object instanceof SourceLink), object);
  2457.         if (object instanceof Firebug.SourceFile)
  2458.             return (object.href in this.context.sourceFileMap);
  2459.         else if (object instanceof SourceLink)
  2460.             return (object.href in this.context.sourceFileMap);
  2461.         else if (object instanceof jsdIStackFrame)
  2462.             return (normalizeURL(object.script.fileName) in this.context.sourceFileMap);
  2463.         else if (object instanceof "function")
  2464.             return false; //TODO
  2465.     },
  2466.  
  2467.     refresh: function()  // delete any sourceBox-es that are not in sync with sourceFiles
  2468.     {
  2469.         for(var url in this.sourceBoxes)
  2470.         {
  2471.             if (this.sourceBoxes.hasOwnProperty(url))
  2472.             {
  2473.                 var sourceBox = this.sourceBoxes[url];
  2474.                 var sourceFile = this.context.sourceFileMap[url];
  2475.                 if (sourceFile != sourceBox.repObject)
  2476.                 {
  2477.                     var victim = this.sourceBoxes[url];
  2478.                     delete this.sourceBoxes[url];
  2479.                     if (this.selectedSourceBox == victim)
  2480.                         this.showSourceFile(sourceFile);
  2481.                 }
  2482.             }
  2483.         }
  2484.  
  2485.         if (!this.selectedSourceBox)  // then show() has not run, but we have to refresh, so do the default.
  2486.             this.navigate();
  2487.     },
  2488.  
  2489.     updateLocation: function(sourceFile)
  2490.     {
  2491.         if (!sourceFile)
  2492.             return;  // XXXjjb do we need to show a blank?
  2493.  
  2494.         // Since our last use of the sourceFile we may have compiled or recompiled the source
  2495.         var updatedSourceFile = this.context.sourceFileMap[sourceFile.href];
  2496.         if (!updatedSourceFile)
  2497.             updatedSourceFile = this.getDefaultLocation(this.context);
  2498.         if (!updatedSourceFile)
  2499.             return;
  2500.  
  2501.         this.showSourceFile(updatedSourceFile);
  2502.         dispatch([Firebug.A11yModel], "onUpdateScriptLocation", [this, updatedSourceFile]);
  2503.     },
  2504.  
  2505.     updateSelection: function(object)
  2506.     {
  2507.         if (object instanceof jsdIStackFrame)
  2508.             this.showStackFrame(object);
  2509.         else if (object instanceof Firebug.SourceFile)
  2510.             this.navigate(object);
  2511.         else if (object instanceof SourceLink)
  2512.             this.showSourceLink(object);
  2513.         else if (typeof(object) == "function")
  2514.             this.showFunction(object);
  2515.         else
  2516.             this.showStackFrame(null);
  2517.     },
  2518.  
  2519.     showThisSourceFile: function(sourceFile)
  2520.     {
  2521.         //-----------------------------------123456789
  2522.         if (sourceFile.href.substr(0, 9) == "chrome://")
  2523.             return false;
  2524.  
  2525.            if (sourceFile.isEval() && !this.showEvals)
  2526.                return false;
  2527.  
  2528.         if (sourceFile.isEvent() && !this.showEvents)
  2529.             return false;
  2530.  
  2531.         return true;
  2532.     },
  2533.  
  2534.     getLocationList: function()
  2535.     {
  2536.         var context = this.context;
  2537.  
  2538.         if (!context.onLoadWindowContent) // then context was not active during load
  2539.             this.updateScriptFiles(context);
  2540.  
  2541.         var allSources = sourceFilesAsArray(context.sourceFileMap);
  2542.  
  2543.         if (Firebug.showAllSourceFiles)
  2544.         {
  2545.             return allSources;
  2546.         }
  2547.  
  2548.         var filter = Firebug.getPref(Firebug.servicePrefDomain, "scriptsFilter");
  2549.         this.showEvents = (filter == "all" || filter == "events");
  2550.         this.showEvals = (filter == "all" || filter == "evals");
  2551.  
  2552.         var list = [];
  2553.         for (var i = 0; i < allSources.length; i++)
  2554.         {
  2555.             if (this.showThisSourceFile(allSources[i]))
  2556.                 list.push(allSources[i]);
  2557.         }
  2558.  
  2559.         return list;
  2560.     },
  2561.  
  2562.     updateScriptFiles: function(context, eraseSourceFileMap)  // scan windows for 'script' tags (only if debugger is not enabled)
  2563.     {
  2564.         var oldMap = eraseSourceFileMap ? null : context.sourceFileMap;
  2565.  
  2566.         function addFile(url, scriptTagNumber, dependentURL)
  2567.         {
  2568.                 if (oldMap && url in oldMap)
  2569.                 {
  2570.                     var sourceFile = oldMap[url];
  2571.                     sourceFile.dependentURL = dependentURL;
  2572.                     context.addSourceFile(sourceFile);
  2573.                     return false;
  2574.                 }
  2575.                 else
  2576.                 {
  2577.                     var sourceFile = new Firebug.ScriptTagSourceFile(context, url, scriptTagNumber);
  2578.                     sourceFile.dependentURL = dependentURL;
  2579.                     context.addSourceFile(sourceFile);
  2580.                     return true;
  2581.                 }
  2582.         }
  2583.  
  2584.         iterateWindows(context.window, function updateEachWin(win)
  2585.         {
  2586.             if (!win.document.documentElement)
  2587.                 return;
  2588.  
  2589.             var url = normalizeURL(win.location.href);
  2590.  
  2591.             if (url)
  2592.             {
  2593.                 if (!context.sourceFileMap.hasOwnProperty(url))
  2594.                 {
  2595.                     var URLOnly = new Firebug.NoScriptSourceFile(context, url);
  2596.                     context.addSourceFile(URLOnly);
  2597.                 }
  2598.             }
  2599.  
  2600.             var baseUrl = win.location.href;
  2601.             var bases = win.document.documentElement.getElementsByTagName("base");
  2602.             if (bases && bases[0])
  2603.             {
  2604.                 baseUrl = bases[0].href;
  2605.             }
  2606.  
  2607.             var scripts = win.document.documentElement.getElementsByTagName("script");
  2608.             for (var i = 0; i < scripts.length; ++i)
  2609.             {
  2610.                 var scriptSrc = scripts[i].getAttribute('src'); // for XUL use attribute
  2611.                 var url = scriptSrc ? absoluteURL(scriptSrc, baseUrl) : win.location.href;
  2612.                 url = normalizeURL(url ? url : win.location.href);
  2613.                 var added = addFile(url, i, (scriptSrc?win.location.href:null));
  2614.             }
  2615.         });
  2616.  
  2617.         var notificationURL = "firebug:// Warning. Script Panel was inactive during page load/Reload to see all sources";
  2618.         var dummySourceFile = new Firebug.NoScriptSourceFile(context, notificationURL);
  2619.         context.sourceCache.store(notificationURL, 'reload to see all source files');
  2620.         context.addSourceFile(dummySourceFile);
  2621.         context.notificationSourceFile = dummySourceFile;
  2622.  
  2623.     },
  2624.  
  2625.  
  2626.     getDefaultLocation: function(context)
  2627.     {
  2628.         var sourceFiles = this.getLocationList();
  2629.         if (context)
  2630.         {
  2631.             var url = context.getWindowLocation();
  2632.             for (var i = 0; i < sourceFiles.length; i++)
  2633.             {
  2634.                 if (url == sourceFiles[i].href)
  2635.                     return sourceFiles[i];
  2636.             }
  2637.             return sourceFiles[0];
  2638.         }
  2639.         else
  2640.             return sourceFiles[0];
  2641.     },
  2642.  
  2643.     getDefaultSelection: function(context)
  2644.     {
  2645.         return this.getDefaultLocation(context);
  2646.     },
  2647.  
  2648.     getTooltipObject: function(target)
  2649.     {
  2650.         // Target should be A element with class = sourceLine
  2651.         if ( hasClass(target, 'sourceLine') )
  2652.         {
  2653.             var lineNo = parseInt(target.innerHTML);
  2654.  
  2655.             if ( isNaN(lineNo) )
  2656.                 return;
  2657.             var scripts = this.location.scriptsIfLineCouldBeExecutable(lineNo);
  2658.             if (scripts)
  2659.             {
  2660.                 var str = "scripts ";
  2661.                 for(var i = 0; i < scripts.length; i++)
  2662.                     str += scripts[i].tag +" ";
  2663.                 return str;
  2664.             }
  2665.             else
  2666.                 return new String("no executable script at "+lineNo);
  2667.         }
  2668.         return null;
  2669.     },
  2670.  
  2671.     getPopupObject: function(target)
  2672.     {
  2673.         // Don't show popup over the line numbers, we show the conditional breakpoint
  2674.         // editor there instead
  2675.         var sourceLine = getAncestorByClass(target, "sourceLine");
  2676.         if (sourceLine)
  2677.             return;
  2678.  
  2679.         var sourceRow = getAncestorByClass(target, "sourceRow");
  2680.         if (!sourceRow)
  2681.             return;
  2682.  
  2683.         var lineNo = parseInt(sourceRow.firstChild.textContent);
  2684.         var scripts = findScripts(this.context, this.location.href, lineNo);
  2685.         return scripts; // gee I wonder what will happen?
  2686.     },
  2687.  
  2688.     showInfoTip: function(infoTip, target, x, y, rangeParent, rangeOffset)
  2689.     {
  2690.         var frame = this.context.currentFrame;
  2691.         if (!frame)
  2692.             return;
  2693.  
  2694.         var sourceRowText = getAncestorByClass(target, "sourceRowText");
  2695.         if (!sourceRowText)
  2696.             return;
  2697.  
  2698.         // see http://code.google.com/p/fbug/issues/detail?id=889
  2699.         // idea from: Jonathan Zarate's rikaichan extension (http://www.polarcloud.com/rikaichan/)
  2700.         if (!rangeParent)
  2701.             return;
  2702.         rangeOffset = rangeOffset || 0;
  2703.         var expr = getExpressionAt(rangeParent.data, rangeOffset);
  2704.         if (!expr || !expr.expr)
  2705.             return;
  2706.  
  2707.         if (expr.expr == this.infoTipExpr)
  2708.             return true;
  2709.         else
  2710.             return this.populateInfoTip(infoTip, expr.expr);
  2711.     },
  2712.  
  2713.     getObjectPath: function(frame)
  2714.     {
  2715.         frame = this.context.debugFrame;
  2716.  
  2717.         var frames = [];
  2718.         for (; frame; frame = getCallingFrame(frame))
  2719.             frames.push(frame);
  2720.  
  2721.         return frames;
  2722.     },
  2723.  
  2724.     getObjectLocation: function(sourceFile)
  2725.     {
  2726.         return sourceFile.href;
  2727.     },
  2728.  
  2729.     // return.path: group/category label, return.name: item label
  2730.     getObjectDescription: function(sourceFile)
  2731.     {
  2732.         return sourceFile.getObjectDescription();
  2733.     },
  2734.  
  2735.     getOptionsMenuItems: function()
  2736.     {
  2737.         var context = this.context;
  2738.  
  2739.         return [
  2740.             optionMenu("DecompileEvals", "decompileEvals"),
  2741.             serviceOptionMenu("ShowAllSourceFiles", "showAllSourceFiles"),
  2742.             // 1.2: always check last line; optionMenu("UseLastLineForEvalName", "useLastLineForEvalName"),
  2743.             // 1.2: always use MD5 optionMenu("UseMD5ForEvalName", "useMD5ForEvalName")
  2744.             serviceOptionMenu("TrackThrowCatch", "trackThrowCatch"),
  2745.             //"-",
  2746.             //1.2 option on toolbar this.optionMenu("DebuggerEnableAlways", enableAlwaysPref)
  2747.         ];
  2748.     },
  2749.  
  2750.     optionMenu: function(label, option)
  2751.     {
  2752.         var checked = Firebug.getPref(prefDomain, option);
  2753.         return {label: label, type: "checkbox", checked: checked,
  2754.             command: bindFixed(Firebug.setPref, Firebug, prefDomain, option, !checked) };
  2755.     },
  2756.  
  2757.     getContextMenuItems: function(fn, target)
  2758.     {
  2759.         if (getAncestorByClass(target, "sourceLine"))
  2760.             return;
  2761.  
  2762.         var sourceRow = getAncestorByClass(target, "sourceRow");
  2763.         if (!sourceRow)
  2764.             return;
  2765.  
  2766.         var sourceLine = getChildByClass(sourceRow, "sourceLine");
  2767.         var lineNo = parseInt(sourceLine.textContent);
  2768.  
  2769.         var items = [];
  2770.  
  2771.         var selection = this.document.defaultView.getSelection();
  2772.         if (selection.toString())
  2773.         {
  2774.             items.push(
  2775.                 {label: "CopySourceCode", command: bind(this.copySource, this) },
  2776.                 "-",
  2777.                 {label: "AddWatch", command: bind(this.addSelectionWatch, this) }
  2778.             );
  2779.         }
  2780.  
  2781.         var hasBreakpoint = sourceRow.getAttribute("breakpoint") == "true";
  2782.  
  2783.         items.push(
  2784.             "-",
  2785.             {label: "SetBreakpoint", type: "checkbox", checked: hasBreakpoint,
  2786.                 command: bindFixed(this.toggleBreakpoint, this, lineNo) }
  2787.         );
  2788.         if (hasBreakpoint)
  2789.         {
  2790.             var isDisabled = fbs.isBreakpointDisabled(this.location.href, lineNo);
  2791.             items.push(
  2792.                 {label: "DisableBreakpoint", type: "checkbox", checked: isDisabled,
  2793.                     command: bindFixed(this.toggleDisableBreakpoint, this, lineNo) }
  2794.             );
  2795.         }
  2796.         items.push(
  2797.             {label: "EditBreakpointCondition",
  2798.                 command: bindFixed(this.editBreakpointCondition, this, lineNo) }
  2799.         );
  2800.  
  2801.         if (this.context.stopped)
  2802.         {
  2803.             var sourceRow = getAncestorByClass(target, "sourceRow");
  2804.             if (sourceRow)
  2805.             {
  2806.                 var sourceFile = getAncestorByClass(sourceRow, "sourceBox").repObject;
  2807.                 var lineNo = parseInt(sourceRow.firstChild.textContent);
  2808.  
  2809.                 var debuggr = Firebug.Debugger;
  2810.                 items.push(
  2811.                     "-",
  2812.                     {label: "Continue",
  2813.                         command: bindFixed(debuggr.resume, debuggr, this.context) },
  2814.                     {label: "StepOver",
  2815.                         command: bindFixed(debuggr.stepOver, debuggr, this.context) },
  2816.                     {label: "StepInto",
  2817.                         command: bindFixed(debuggr.stepInto, debuggr, this.context) },
  2818.                     {label: "StepOut",
  2819.                         command: bindFixed(debuggr.stepOut, debuggr, this.context) },
  2820.                     {label: "RunUntil",
  2821.                         command: bindFixed(debuggr.runUntil, debuggr, this.context,
  2822.                         sourceFile, lineNo) }
  2823.                 );
  2824.             }
  2825.         }
  2826.  
  2827.         return items;
  2828.     },
  2829.  
  2830.     getEditor: function(target, value)
  2831.     {
  2832.         if (!this.conditionEditor)
  2833.             this.conditionEditor = new Firebug.Breakpoint.ConditionEditor(this.document);
  2834.  
  2835.         return this.conditionEditor;
  2836.     },
  2837.  
  2838.     breakOnNext: function(enabled)
  2839.     {
  2840.         if (enabled)
  2841.             Firebug.Debugger.suspend(this.context);
  2842.         else
  2843.             Firebug.Debugger.unSuspend(this.context);
  2844.     },
  2845.  
  2846.     getBreakOnNextTooltip: function(armed)
  2847.     {
  2848.         return (armed ? $STR("script.Disable Break On Next") : $STR("script.Break On Next"));
  2849.     },
  2850.  
  2851.     shouldBreakOnNext: function()
  2852.     {
  2853.         var stepMode = fbs.getStepMode();
  2854.         return stepMode && (stepMode == "STEP_SUSPEND");
  2855.     },
  2856. });
  2857.  
  2858. // ************************************************************************************************
  2859.  
  2860. Firebug.Debugger.Breakpoint = function(name, href, lineNumber, checked, sourceLine, isFuture)
  2861. {
  2862.     this.name = name;
  2863.     this.href = href;
  2864.     this.lineNumber = lineNumber;
  2865.     this.checked = checked;
  2866.     this.sourceLine = sourceLine;
  2867.     this.isFuture = isFuture;
  2868. }
  2869.  
  2870. // ************************************************************************************************
  2871.  
  2872. Firebug.DebuggerListener =
  2873. {
  2874.     /*
  2875.      * Called before pausing JSD to allow listeners to prevent the pause
  2876.      * @param rejection an array, push boolean true to cause rejection.
  2877.      */
  2878.     onPauseJSDRequested: function(rejection)
  2879.     {
  2880.     },
  2881.  
  2882.     /*
  2883.      * @param active the current value of  (jsd && jsd.isOn && (jsd.pauseDepth == 0) )
  2884.      * @param why a string explaining the change
  2885.      */
  2886.     onJSDActivate: function(active, why)  // start or unPause
  2887.     {
  2888.  
  2889.     },
  2890.  
  2891.     /*
  2892.      * @param active the current value of  (jsd && jsd.isOn && (jsd.pauseDepth == 0) )
  2893.      * @param why a string explaining the change
  2894.      */
  2895.     onJSDDeactivate: function(active, why) // stop or pause
  2896.     {
  2897.  
  2898.     },
  2899.  
  2900.     onStop: function(context, frame, type, rv)
  2901.     {
  2902.     },
  2903.  
  2904.     onResume: function(context)
  2905.     {
  2906.     },
  2907.  
  2908.     onThrow: function(context, frame, rv)
  2909.     {
  2910.         return false; /* continue throw */
  2911.     },
  2912.  
  2913.     onError: function(context, frame, error)
  2914.     {
  2915.     },
  2916.  
  2917.     onEventScriptCreated: function(context, frame, url, sourceFile)
  2918.     {
  2919.     },
  2920.  
  2921.     onTopLevelScriptCreated: function(context, frame, url, sourceFile)
  2922.     {
  2923.     },
  2924.  
  2925.     onEvalScriptCreated: function(context, frame, url, sourceFile)
  2926.     {
  2927.     },
  2928.  
  2929.     onFunctionConstructor: function(context, frame, ctor_script, url, sourceFile)
  2930.     {
  2931.     },
  2932. };
  2933.  
  2934. // ************************************************************************************************
  2935.  
  2936. function CallstackPanel() { }
  2937.  
  2938. CallstackPanel.prototype = extend(Firebug.Panel,
  2939. {
  2940.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2941.     // extends Panel
  2942.  
  2943.     name: "callstack",
  2944.     parentPanel: "script",
  2945.     order: 1,
  2946.  
  2947.     initialize: function(context, doc)
  2948.     {
  2949.         var panelStatus = Firebug.chrome.getPanelStatusElements();
  2950.         this.onPanelStatusSelectItem = bind(this.onSelectItem, this);
  2951.         panelStatus.addEventListener('selectItem', this.onPanelStatusSelectItem, false);
  2952.  
  2953.         Firebug.Panel.initialize.apply(this, arguments);
  2954.     },
  2955.  
  2956.     destroy: function(state)
  2957.     {
  2958.         var panelStatus = Firebug.chrome.getPanelStatusElements();
  2959.         panelStatus.removeEventListener('selectItem', this.onPanelStatusSelectItem, false);
  2960.  
  2961.         Firebug.Panel.destroy.apply(this, arguments);
  2962.     },
  2963.  
  2964.     initializeNode : function(oldPanelNode)
  2965.     {
  2966.         dispatch([Firebug.A11yModel], 'onInitializeNode', [this, 'console']);
  2967.     },
  2968.  
  2969.     destroyNode : function()
  2970.     {
  2971.         dispatch([Firebug.A11yModel], 'onDestroyNode', [this, 'console']);
  2972.     },
  2973.  
  2974.     show: function(state)
  2975.     {
  2976.           this.refresh();
  2977.     },
  2978.  
  2979.     supportsObject: function(object)
  2980.     {
  2981.         return object instanceof jsdIStackFrame;
  2982.     },
  2983.  
  2984.     updateSelection: function(object)
  2985.     {
  2986.         if (object instanceof jsdIStackFrame)
  2987.             this.showStackFrame(object);
  2988.     },
  2989.  
  2990.     refresh: function()
  2991.     {
  2992.         var mainPanel = this.context.getPanel("script", true);
  2993.         if (mainPanel)
  2994.         {
  2995.             if (mainPanel.selection instanceof jsdIStackFrame)
  2996.                 this.showStackFrame(mainPanel.selection);
  2997.         }
  2998.         else
  2999.         {
  3000.         }
  3001.     },
  3002.  
  3003.     showStackFrame: function(frame)
  3004.     {
  3005.         clearNode(this.panelNode);
  3006.         var mainPanel = this.context.getPanel("script", true);
  3007.  
  3008.         if (mainPanel && frame)
  3009.         {
  3010.             FBL.setClass(this.panelNode, "objectBox-stackTrace");
  3011.             // The panelStatus has the stack, lets reuse it to give the same UX as that control.
  3012.             // TODO use domplate? Use the panel status directly?
  3013.             var panelStatus = Firebug.chrome.getPanelStatusElements();
  3014.             var frameButtons = panelStatus.getElementsByTagName("toolbarbutton");
  3015.             var doc = this.panelNode.ownerDocument;
  3016.             for (var i = 0; i < frameButtons.length; i++)
  3017.             {
  3018.                 if (FBL.hasClass(frameButtons[i], "panelStatusLabel"))
  3019.                 {
  3020.                     var div = doc.createElement("div");
  3021.                     var frameButton = frameButtons[i];
  3022.                     div.innerHTML = frameButton.getAttribute('label');
  3023.                     if (frameButton.repObject instanceof jsdIStackFrame)  // causes a downcast
  3024.                         div.repObject = frameButton.repObject;
  3025.                     div.frameButton = frameButton;
  3026.                     FBL.setClass(div, "objectLink");
  3027.                     FBL.setClass(div, "objectLink-stackFrame");
  3028.                     FBL.setClass(div, "panelStatusLabel");
  3029.                     FBL.setClass(div, "focusRow");
  3030.                     div.setAttribute('role', "listitem");
  3031.  
  3032.                     if (frameButton.getAttribute("selected") == "true")
  3033.                         this.selectItem(div);
  3034.  
  3035.                     this.panelNode.appendChild(div);
  3036.                 }
  3037.             }
  3038.             dispatch([Firebug.A11yModel], 'onstackCreated', [this]);
  3039.         }
  3040.     },
  3041.  
  3042.     onSelectItem: function(event)
  3043.     {
  3044.         for (var child = this.panelNode.firstChild; child; child = child.nextSibling)
  3045.         {
  3046.             if (child.frameButton.getAttribute("selected") == "true")
  3047.             {
  3048.                 this.selectItem(child);
  3049.                 return;
  3050.             }
  3051.         }
  3052.  
  3053.     },
  3054.  
  3055.     selectItem: function(item)
  3056.     {
  3057.         if (this.selectedItem)
  3058.             this.selectedItem.removeAttribute("selected");
  3059.  
  3060.         this.selectedItem = item;
  3061.  
  3062.         if (item)
  3063.             item.setAttribute("selected", "true");
  3064.     },
  3065.  
  3066.     getOptionsMenuItems: function()
  3067.     {
  3068.         var items = [
  3069.             optionMenu("OmitObjectPathStack", "omitObjectPathStack"),  // an option handled by chrome.js
  3070.             ];
  3071.         return items;
  3072.     }
  3073. });
  3074.  
  3075. // ************************************************************************************************
  3076.  
  3077. function getCallingFrame(frame)
  3078. {
  3079.     try
  3080.     {
  3081.         do
  3082.         {
  3083.             frame = frame.callingFrame;
  3084.             if (!(Firebug.filterSystemURLs && isSystemURL(normalizeURL(frame.script.fileName))))
  3085.                 return frame;
  3086.         }
  3087.         while (frame);
  3088.     }
  3089.     catch (exc)
  3090.     {
  3091.     }
  3092.     return null;
  3093. }
  3094.  
  3095.  
  3096. function getFrameScopeWindowAncestor(frame)  // walk script scope chain to bottom, null unless a Window
  3097. {
  3098.     var scope = frame.scope;
  3099.     if (scope)
  3100.     {
  3101.         while(scope.jsParent)
  3102.             scope = scope.jsParent;
  3103.  
  3104.         if (scope.jsClassName == "Window" || scope.jsClassName == "ChromeWindow" || scope.jsClassName == "ModalContentWindow")
  3105.             return new XPCNativeWrapper(scope.getWrappedValue());
  3106.  
  3107.         if (scope.jsClassName == "Sandbox")
  3108.         {
  3109.             var proto = scope.jsPrototype;
  3110.             if (proto.jsClassName == "XPCNativeWrapper")
  3111.                 proto = proto.jsParent;
  3112.             if (proto.jsClassName == "Window")
  3113.                 return new XPCNativeWrapper(proto.getWrappedValue());
  3114.         }
  3115.  
  3116.     }
  3117.     else
  3118.         return null;
  3119. }
  3120.  
  3121. function getFrameWindow(frame)
  3122. {
  3123.     var result = {};
  3124.     if (frame.eval("window", "", 1, result))
  3125.     {
  3126.         var win = unwrapIValue(result.value);
  3127.         return getRootWindow(win);
  3128.     }
  3129. }
  3130.  
  3131.  
  3132. function ArrayEnumerator(array)
  3133. {
  3134.     this.index = 0;
  3135.     this.array = array;
  3136.     this.hasMoreElements = function()
  3137.     {
  3138.         return (this.index < array.length);
  3139.     }
  3140.     this.getNext = function()
  3141.     {
  3142.         return this.array[++this.index];
  3143.     }
  3144. }
  3145.  
  3146. // ************************************************************************************************
  3147.  
  3148. Firebug.registerActivableModule(Firebug.Debugger);
  3149. Firebug.registerPanel(CallstackPanel);
  3150. Firebug.registerPanel(Firebug.ScriptPanel);
  3151.  
  3152. // ************************************************************************************************
  3153. }});
  3154.